近期在梳理項目中的 手機(jī)圖片處理荠商、文件處理皆撩、APK文件在線升級等功能時扣墩,發(fā)現(xiàn)了在個別小米手機(jī)上總是報錯:
Unable to load resource 0x00000000 from pkg=com.android.systemui
Unable to load resource 0x00000000 from pkg=com.android.systemui
android.content.res.Resources$NotFoundException: Resource ID #0x0
at android.content.res.ResourcesImpl.getValue(ResourcesImpl.java:201)
at android.content.res.MiuiResourcesImpl.getValue(MiuiResourcesImpl.java:94)
at android.content.res.Resources.getDrawable(Resources.java:788)
at android.graphics.drawable.Icon.loadDrawableInner(Icon.java:316)
at android.graphics.drawable.Icon.loadDrawable(Icon.java:272)
at android.graphics.drawable.Icon.loadDrawableAsUser(Icon.java:380)
at com.android.systemui.statusbar.ExpandedIcon.getDrawable(ExpandedIcon.java:59)
at com.android.systemui.statusbar.StatusBarIconView.getIcon(StatusBarIconView.java:179)
at com.android.systemui.statusbar.StatusBarIconView.setIcon(StatusBarIconView.java:138)
at com.android.systemui.statusbar.StatusBarIconView.updateDarkMode(StatusBarIconView.java:271)
at com.android.systemui.statusbar.phone.SimpleStatusBar.updateDarkMode(SimpleStatusBar.java:291)
at com.android.systemui.statusbar.phone.PhoneStatusBar$19.run(PhoneStatusBar.java:3730)
at android.os.Handler.handleCallback(Handler.java:754)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:163)
at android.app.ActivityThread.main(ActivityThread.java:6363)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:904)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:794)
關(guān)于該問題,網(wǎng)上討論的帖子很多扛吞,也有很多的解釋呻惕。
該異常恐怕只有小米系統(tǒng)開發(fā)人員才知道:在哪些具體條件下滥比,會報出來亚脆。
而我們的問題,主要是處理文件時出現(xiàn)的守呜。那么型酥,此文章也只是針對手機(jī)文件處理時給出一些解決方案。
分析:
- 經(jīng)測試查乒,小米個別機(jī)型高低版本(Android版本弥喉,如android7.1.2等)都有可能出現(xiàn)該問題。
- 問題的主要原因:使用Intent傳遞參數(shù)時玛迄,直接把大的數(shù)據(jù)作為了參數(shù)進(jìn)行傳遞由境,如文件。
調(diào)用系統(tǒng)程序蓖议,裁切指定的圖片虏杰,小米手機(jī)報此異常
// 裁切圖
public void cutimage(Uri imageUri) {
Intent intent= new Intent("com.android.camera.action.CROP");
intent.setDataAndType(imageUri, "image/*");
......
......
intent.putExtra("outputFormat", "JPEG");
intent.putExtra("noFaceDetection", true);
intent.putExtra("return-data", true);
startActivityForResult(intent, AppConstants.REQUEST_CODE_CROP);
}
調(diào)用系統(tǒng)程序,安裝下載的apk文件時勒虾,小米手機(jī)報此異常
/**
* apk安裝
*/
public static void InstallApk(Context context, File apkFile){
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction(android.content.Intent.ACTION_VIEW);
intent.setDataAndType(FileProviderUtils.uriFromFile(context, apkFile),
"application/vnd.android.package-archive");
context.startActivity(intent);
}
setDataAndType方法傳遞了安裝包數(shù)據(jù)時纺阔,報異常;
除此之外修然,還有很多例子笛钝,此處不再羅列。
原因:
通過網(wǎng)上查詢資料愕宋、代碼實驗玻靡,這個問題應(yīng)該是:因為小米個別機(jī)型使用Intent傳遞過大的數(shù)據(jù)導(dǎo)致的。
intent.setDataAndType(imageUri, "image/*");
該方法傳遞的是uri中贝,不是文件囤捻,沒有問題;
Intent.putExtra("return-data",true)邻寿;
該方法是指否回傳數(shù)據(jù)蝎土。
如果設(shè)置為true视哑,當(dāng)處理完畢后,結(jié)果會通過intent進(jìn)行回傳誊涯,如果處理的是大文件黎炉,那就要小心了!4着 !
小米系統(tǒng)問題可能就是出在這里淀弹,return-data的方式只適用于小數(shù)據(jù)丹壕,小米手機(jī)有可能處理完的數(shù)據(jù)仍然過大,導(dǎo)致的異常薇溃。
解決方案
Intent傳參時菌赖,不直接傳遞大的文件數(shù)據(jù)。
重點1:
那么沐序,我們的數(shù)據(jù)應(yīng)該如何傳遞呢琉用? --以上面的例子為參考,我們進(jìn)行分析策幼。
如果我們需要處理后的數(shù)據(jù)(如裁切后的圖片)邑时,可以使用URI回傳,而非實際的數(shù)據(jù)特姐;
如果我們不需要處理好的數(shù)據(jù)晶丘,直接設(shè)置不需要回傳即可。
但是這兩種方式唐含,都需要明確設(shè)置Intent.putExtra("return-data",false)浅浮;
重點2:
我們?nèi)魏畏较騻鬟f文件都需要使用URI,但是使用時捷枯,要小心一個坑滚秩,即android7.0傳遞uri時,應(yīng)注意權(quán)限安全淮捆。
即:針對URI郁油,我們要分版本適配,網(wǎng)上有很多處理方案争剿,也可參考已艰,我之前寫的文章:
http://www.reibang.com/p/bec4497c2a63
代碼實現(xiàn)
代碼也同樣以上面的例子為參考,給予解決:
代碼1:
URI 適配封裝類:
package com.iwangzhe.app.util.android7.urifit;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.support.v4.content.FileProvider;
import java.io.File;
/**
* 類:FileProviderUtils Uri適配幫助類
* 從APP向外共享的文件URI時蚕苇,必須使用該類進(jìn)行適配哩掺,否則在7.0以上系統(tǒng),會報錯:FileUriExposedException(文件Uri暴露異常)
* 作者: qxc
* 日期:2018/2/23.
*/
public class FileProviderUtils {
/**
* 從文件獲得URI
* @param context 上下文
* @param file 文件
* @return 文件對應(yīng)的URI
*/
public static Uri uriFromFile(Context context, File file) {
Uri fileUri;
//7.0以上進(jìn)行適配
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
String p = context.getPackageName() + ".FileProvider";
fileUri = FileProvider.getUriForFile(
context,
p,
file);
} else {
fileUri = Uri.fromFile(file);
}
return fileUri;
}
/**
* 設(shè)置Intent的data和類型涩笤,并賦予目標(biāo)程序臨時的URI讀寫權(quán)限
* @param context 上下文
* @param intent 意圖
* @param type 類型
* @param file 文件
* @param writeAble 是否賦予可寫URI的權(quán)限
*/
public static void setIntentDataAndType(Activity context,
Intent intent,
String type,
File file,
boolean writeAble) {
//7.0以上進(jìn)行適配
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.setDataAndType(uriFromFile(context, file), type);
//臨時賦予讀寫Uri的權(quán)限
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
if (writeAble) {
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
} else {
intent.setDataAndType(Uri.fromFile(file), type);
}
}
/**
* 設(shè)置Intent的data和類型嚼吞,并賦予目標(biāo)程序臨時的URI讀寫權(quán)限
* @param context 上下文
* @param intent 意圖
* @param type 類型
* @param fileUri 文件uri
* @param writeAble 是否賦予可寫URI的權(quán)限
*/
public static void setIntentDataAndType(Context context,
Intent intent,
String type,
Uri fileUri,
boolean writeAble) {
//7.0以上進(jìn)行適配
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.setDataAndType(fileUri, type);
//臨時賦予讀寫Uri的權(quán)限
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
if (writeAble) {
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
} else {
intent.setDataAndType(fileUri, type);
}
}
}
代碼2:
系統(tǒng)程序交互類(調(diào)用裁切盒件、執(zhí)行安裝包等等)
package com.iwangzhe.app.util.android7.systemprogram;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.provider.MediaStore;
import com.iwangzhe.app.util.android7.urifit.FileProviderUtils;
import com.iwangzhe.app.util.log.collect.exception.ExceptionProxy;
import java.io.File;
/**
* 類:SystemProgramUtils 系統(tǒng)程序適配幫助類
* 1. 拍照
* 2. 相冊
* 3. 裁切
* 4. apk安裝
* 作者: qxc
* 日期:2018/2/23.
*/
public class SystemProgramUtils {
public static final int REQUEST_CODE_PAIZHAO = 1;
public static final int REQUEST_CODE_ZHAOPIAN = 2;
public static final int REQUEST_CODE_CAIQIE = 3;
/**
* 打開相機(jī)拍照
*/
public static void paizhao(Activity activity, File outputFile){
Intent intent = new Intent();
intent.setAction("android.media.action.IMAGE_CAPTURE");
intent.addCategory("android.intent.category.DEFAULT");
Uri uri = FileProviderUtils.uriFromFile(activity, outputFile);
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
activity.startActivityForResult(intent, REQUEST_CODE_PAIZHAO);
}
/**
* 打開相冊
*/
public static void zhaopian(Activity activity){
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction("android.intent.action.PICK");
intent.addCategory("android.intent.category.DEFAULT");
activity.startActivityForResult(intent, REQUEST_CODE_ZHAOPIAN);
}
/**
* 打開圖片裁切
*/
public static void Caiqie(Activity activity, Uri uri, File outputFile) {
Intent intent = new Intent("com.android.camera.action.CROP");
FileProviderUtils.setIntentDataAndType(activity, intent, "image/*", uri, true);
intent.putExtra("crop", "true");
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
intent.putExtra("outputX", 300);
intent.putExtra("outputY", 300);
//return-data為true時,直接返回bitmap舱禽,可能會很占內(nèi)存炒刁,不建議,小米等個別機(jī)型會出異常L苤伞O枋肌!
//所以適配小米等個別機(jī)型里伯,裁切后的圖片城瞎,不能直接使用data返回,應(yīng)使用uri指向
//裁切后保存的URI疾瓮,不屬于我們向外共享的脖镀,所以可以使用fill://類型的URI
Uri outputUri = Uri.fromFile(outputFile);
intent.putExtra(MediaStore.EXTRA_OUTPUT, outputUri);
intent.putExtra("return-data", false);
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
intent.putExtra("noFaceDetection", true);
activity.startActivityForResult(intent, REQUEST_CODE_CAIQIE);
}
/**
* apk安裝
*/
public static void InstallApk(Context context, File apkFile){
try {
//設(shè)置權(quán)限
String command = "chmod 777 " + apkFile.getPath();
Runtime runtime = Runtime.getRuntime();
runtime.exec(command);
//安裝apk
Intent intent = new Intent();
FileProviderUtils.setIntentDataAndType(context, intent, "application/vnd.android.package-archive", FileProviderUtils.uriFromFile(context, apkFile), true);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction(android.content.Intent.ACTION_VIEW);
intent.putExtra("return-data", false);
context.startActivity(intent);
}catch (Exception ex){
ExceptionProxy.catchException(ex);
}
}
}
裁切方法注意點:
Uri outputUri = Uri.fromFile(outputFile);
intent.putExtra(MediaStore.EXTRA_OUTPUT, outputUri);
intent.putExtra("return-data", false);
這幾句代碼,就是告訴裁切程序狼电,當(dāng)裁切完成后蜒灰,不用直接返回數(shù)據(jù),而是把裁切的圖片輸出到outputUri上肩碟,我們自己會訪問該文件獲得裁切后的數(shù)據(jù)强窖。
安裝APK注意點:
1 安裝apk前,應(yīng)保證對該apk有操作的權(quán)限削祈,如不設(shè)置毕骡,個別機(jī)型手機(jī)可能報:無法解析安裝包
2 FileProviderUtils.setIntentDataAndType是我們封裝的7.0URI適配的方法
3 intent.putExtra("return-data", false);明確告訴系統(tǒng),我們不需要返回處理后的數(shù)據(jù)岩瘦;
代碼3:
調(diào)用
//獲得apk文件
File file = getApk();//注意:這是我們自己的方法未巫,不要理解成系統(tǒng)的方法
//安裝apk文件
SystemProgramUtils.InstallApk(context, file);
以上方法,我們進(jìn)行過測試启昧,使用時叙凡,請大家酌情優(yōu)化。
如果有問題密末,請留言或自行查詢資料N找!严里!