欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

安卓问题

程序员文章站 2022-03-27 08:54:03
线程、通信、网络问题java.lang.RuntimeException: Parcel: unable to marshal value接口回调传递参数丢失问题原因:在继承Parcel类中,需要读或写其他自定义类数据,这些自定义类数据需要实现Serializable序列化接口在继承Serializabel类中,需要读或写其他自定义类,这些自定义类数据含有的对象类没有继承Serializable接口Gson解析,字段比bean多或少。导致json字符串解析不出来内容的问题此方法需要json...

线程、通信、网络问题

java.lang.RuntimeException: Parcel: unable to marshal value

接口回调传递参数丢失问题

原因:

  1. 在继承Parcel类中,需要读或写其他自定义类数据,这些自定义类数据需要实现Serializable序列化接口
  2. 在继承Serializabel类中,需要读或写其他自定义类,这些自定义类数据含有的对象类没有继承Serializable接口

Gson解析,字段比bean多或少。导致json字符串解析不出来内容的问题

此方法需要json数据和bean属性一一对应

JsonObject bsqJson = new JsonParser().parse(commonModel.getJsonData().toString()).getAsJsonObject();
BsqBaseInfoModel bsqModel = new Gson().fromJson(bsqJson, BsqBaseInfoModel.class);

此方法不需json数据和bean属性一一对应

List<BsqBaseInfoModel> result=new Gson().fromJson(commonModel.getJsonData().toString(), new TypeToken<List<BsqBaseInfoModel>>(){}.getType());

包:

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;

转json字符串出现"$ref"的问题

问题:对象转json字符串,断点取到json字符串中带"$ref"等字样,不是实际的值的问题。(有出现debug正常,release异常的情况)

解决:升级了fastjson版本导致的,使用Gson解析方式。

Gson gson = new Gson();
String idsStr = gson.toJson(submitBean);

Can’t create handler inside thread that has not called Looper.prepare()

现象:报错Can't create handler inside thread that has not called Looper.prepare()

原因:在子线程中new Handler(加Toast也有这个问题)导致的

分析:因为Handler与其调用者在同一线程中,Handler中执行了延迟操作,会导致调用的线程阻塞。每个Hander对象都会绑定一个Looper对象,每个Looper对象对应一个消息队列(MessageQueue),若在创建Handler时不指定与其绑定的Looper对象则默认将当前线程的Looper绑定到该Handler上。

在主线程中直接使用new Hanlder()创建Handler对象,其将自动与主线程的Looper对象绑定;在子线程中直接new创建会报错,因为子线程中没有开启Looper,而Handler对象必须绑定Looper对象。

解决:

方案一:需先在该子线程中手动开启Looper(Looper.prepare()-->Looper.loop()),然后将其绑定到Handler对象上。

final Runnable runnable = new Runnable(){
	@Override
	public void run(){
		//执行耗时操作
		try{
			Log.e("bm", "runnable线程:"+ Thread.currentThread().getId() + " name:" + Thread.currentThread().getName());
			Thread.sleep(2000L);
			Log.e("bm", "执行完耗时操作了~");
		} catch (InterruptedException e){
			e.printStackTrace();
		}
	}
};

new Thread(){
	public void run(){
		Looper.prepare();
		new Handler().post(runnable);//在子线程中直接去new一个handler
		Looper.loop();//这种情况下,Runnable对象时运行在子线程中的,可以进行耗时操作,但是不能更新UI
	}
}.start();

方案二:通过Looper.getMainLooper(),获得主线程的Looper,将其绑定到此Handler对象上。

final Runnable runnable = new Runnable() {
  @Override
  public void run() {
    //执行耗时操作
    try {
      Log.e("bm", "runnable线程: " + Thread.currentThread().getId()+ " name:" + Thread.currentThread().getName());
      Thread.sleep(2000);
      Log.e("bm", "执行完耗时操作了~");
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
  }
};
new Thread() {
  public void run() {
    new Handler(Looper.getMainLooper()).post(runnable);//在子线程中直接去new 一个handler

    //这种情况下,Runnable对象是运行在主线程中的,不可以进行联网操作,但是可以更新UI
  }
}.start();

数据存储相关问题

Android 也需要防止 SQL 注入

原文链接

背景:

在移动端发现一个用户无法登陆的 bug,经排查,发现是不规范的 Sql 语句以及没做 SQL 攻击的防止导致的。

出现的原因及分析

大多数情况下,我们不需要考虑移动端 SQL 攻击,因为 Android SDK提供的 query/insert/update/delete 等 API 是安全的,但是execSql 这个 API 却不是安全的,尽量别使用这个 API

由于我们的代码,原先的数据存储是靠C++的sqlite,而没借助Android的Sdk来做的。因此,原先一些SQL可能存在问题。

例子

String userName = "";
int age = 24;
mDBHelper.getWritableDatabase().execSQL(String.format(Locale.getDefault(),"insert into " + 		UserDBHelper.TABLE_NAME+ " (UserName,Age) values(%s,%d)",userName,age));

类似上面的代码,我想 大家的项目里或许也有。看上去,这个没有任何问题,但是却存在sql攻击,当我把userName这个字符串的内容换成,带特殊字符的,如 guolei’ 的时候,这个拼接出来的sql语句就有问题了,回报如下错误。

Process: com.guolei.sqlinsert, PID: 22520
                                                                      android.database.sqlite.SQLiteException: unrecognized token: "',24)" (code 1): , while compiling: insert into user (UserName,Age) values(error',24)
                                                                          at android.database.sqlite.SQLiteConnection.nativePrepareStatement(Native Method)

这是因为单引号 ‘ 使sql语句发生了截断,在sql语句中,要用连续两个单引号去表达一个单引号字符。这仅仅是一个例子。有兴趣的,大家去了解SQL攻击相关的东西吧。

四大组件相关问题

BroadcastReceiver

动态注册Receiver风险

使用BroadcastReceiver组件需要动态注册或者静态注册,如果动态注册广播,即在代码中使用registerReceiver()方法注册BroadcastReceiver,只有当registerReceiver()的代码执行到了才进行注册,取消时则调用unregisterReceiver()方法。但registerReceiver()方法注册的BroadcastReceiver是全局的并且默认可导出的,如果没有限制访问权限,可以被任意外部APP访问,向其传递Intent来执行特定的功能。因此,动态注册的BroadcastReceive可能会导致拒绝服务攻击、APP数据泄漏或是越权调用等安全风险。

解决方案

1:在 AndroidManifest.xml 文件中使用静态注册 BroadcastReceiver,同时设置 exported=“false”。(关于这个属性后面会说)

2:必须动态注册 BroadcastReceiver时,使用registerReceiver(BroadcastReceiver,IntentFilter,broadcastPermission,android.os.Handle)函数注册。

3:Android8.0新特性想要支持静态广播、需要添加intent.setComponent(new ComponentName()),详情可以自行查阅。

公共组件配置风险

Activity、Service、Provider、Receiver四大组件若配置为android:exported =”true”,将可以被外部应用调用,这样存在安全隐患的风险。

解决方案

在应用的AndroidManifest.xml文件中,设置组件的android:exported 属性为false或者通过设置自定义权限来限制对这些组件的访问。值得一提的是,若部分功能使用前提是配置必须使用exported为true,这种情况开发者应该根据实际情况来进行集成。

Fragment相关问题

关于fragment的onHidden的问题

  1. 这个只有在hide,show的时候会触发 在add 的时候是不会触发的 首次加载的时候是不触发的onHiddenChanged

  2. activity 上有4个fragment 当前的fragment 为A activity finish掉的时候, B fragment不走生命周期的 除非是已经被调用.add的时候才会走生命周期

关于fragment的onHiddenChanged+onResume+问题

  1. viewpager+fragment 情况:

onHiddenOnChange()+onResume()的区别:

第一次可见的时候生命周期都是onHiddenOnChange–>onResume

其它都只走onHiddenOnChange。除非是activity切换了走了生命周期,这个时候会走onPause跟onResume但是不走onHiddenOnChange

  1. 不带有viewpager情况:

Activity has been destroyed

//在 activity 的 onCreate 中
mFragmentManager.beginTransaction()
	.add(resId, new OneFragment())
	//这里会报异常,activity has been destroyed
	.commit();

原因:Fragmenttransaction也是在 onCreate 中初始化,也许是异步初始化。这就涉及到执行顺序问题。
解决:add(redId,Fragment)放到onCreate()后的生命周期onResume()

DilogFragment生命周期和和设置布局大小无效问题

FragmentManag.beginTransaction().add(Res,fragment).commit 报错 Activity has been destroyed

java.lang.RuntimeException: Unable to start activity ComponentInfo{}java.lang.IllegalStateException: Activity has been destroyed

Caused by: java.lang.IllegalStateException: Activity has been destroyed
                           at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1560)
                           at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:696)
                           at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:662)
fragment = new OneFragment();
mFragmentManager.beginTransaction()
    .add(com.handsomeyang.mango.R.id.root_relative_layout, fragment)
    //这里会报异常,activity has been destroyed
    .commit();

当我在Activity 的onCreate 中初始化fragment 并且用事务添加到Activity 的时候报错,说是Activity 已被销毁,然而怎么也不能理解,commit 源码点进去没找到,百度了好一会,在* 上一个出现类似问题的评论中发现一句话: 说是Fragmentranscation 也是在onCrate中初始化的,也许是异步初始化,这样就涉及到代码执行顺序问题,最好把addFragment 往后提一提,我把这段同样的代码写在OnResume中就能顺利添加上fragment 并且不报错了。

getActivity() 有时候为null

版本升级、apk安装、发布相关问题

安装相关问题

安装失败

  • 失败原因:应用组件的命名与已安装应用有冲突

    • 可能是包名问题(已安装 app 有相同包名)

      解决:需要重新定一个包名,同时在相关第三方平台上要改掉包名

    • 可能是provider的authorities问题(已安装的 app 有相同的 authorities)

    解决:正在安装的 app 的 authorities 改另一个名称(随便改,可以带上 applicationId)

解析包出现错误

  • 可能问题:包名有大写
  • 可能问题:下载下来的apk与服务器的apk大小不一样
/** 
 * @param path下载地址 
 * @param filePath存储路径 
 * @param progressDialog进度条 
 * @return 
 * @throws Exception 
 */  
public static File getFile(String path,String filePath,ProgressDialog progressDialog) throws Exception{
		URL url = new URL(path);  
    HttpURLConnection connection = (HttpURLConnection) url.openConnection();  
    connection.setConnectTimeout(2000);  
    connection.setRequestMethod("GET");  
    if(connection.getResponseCode() == 200){  
    		int total = connection.getContentLength();  
        progressDialog.setMax(total);
        
        InputStream is = connection.getInputStream();//获得socket输入流  
        File file = new File(filePath);  
        FileOutputStream fos = new FileOutputStream(file);//file输出流  
        byte[] buffer = new byte[1024];  
        int len;  
        int progress = 0;  
        while((len = is.read(buffer)) != -1){  
        		fos.write(buffer);  
            progress += len;  
            progressDialog.setProgress(progress);  
        }  
        fos.flush();  
        is.close();  
        fos.close();  
        connection.disconnect();  
        return file;  
    }
    return null;  
}

解决:

byte[] buffer = new byte[1024];

这行代码,把1024改成了128,又改成了64,结果就没问题了。

  • 可能问题:已经安装完毕,点“返回“报错是代码中在安装完毕后加了回调 startActivityForResult…

解决:在安装完毕后加 return

// 安装apk
private void installApk(File file) {
	Intent intent = new Intent(Intent.ACTION_VIEW);
	if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
		intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
		Uri contentUri = FileProvider.getUriForFile(activity, BuildConfig.APPLICATION_ID + ".updateProvider", file);
		intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
		//兼容8.0-26
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
			AppUtils.installApp(file, BuildConfig.APPLICATION_ID + ".updateProvider");
			//兼容8.0以上的安装完毕需要加return,否则有安装成功但是弹出“解析包失败”的页面来误导用户
			return;
		}
	} else {
		intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
		intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
	}
	activity.startActivityForResult(intent, 999);
}

安卓8下不跳转安装页问题

低版本的blankj.utilcode(如"com.blankj:utilcode:1.10.0")需要在App.java中添加初始化操作。

在升级页面VersionUpgradePresenter.java的安装apk逻辑中添加

//兼容8.0 (API26)
if(Build.VERSION.SDDK_INT > Build.VERSION_CODES.N){
    AppUtils.installApp(file, BuildConfig.APPLICATION_ID + ".updateProvider");
}

完整安装apk的代码:

protected void installApk(File file) {
    //这是在 com.blankj:utilcode:1.10.0版本下的处理方式
//    Intent intent = new Intent(Intent.ACTION_VIEW);
//    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
//        intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
//        Uri contentUri = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".updateProvider", file);
//        intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
//        // 兼容8.0-26---新增兼容代码,里面使用到了BlankJ的工具类库
//        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//            AppUtils.installApp(file, BuildConfig.APPLICATION_ID + ".updateProvider");
//        }
//    } else {
//        intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
//        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//    }
//    startActivityForResult(intent, INSTALL_DG_APP_REQUEST_CODE);
    //在高版本 com.blankj:utilcode:1.23.4以上直接用以下语句即可
    AppUtils.installApp(this,file,INSTALL_DG_APP_REQUEST_CODE);
}

打包相关问题

:app:transformClassesAndResourcesWithR8ForRelease

问题:AS打包卡在app:transformClassesAndResourcesWithR8ForRelease

卡在app:transformClassesAndResourcesWithR8ForRelease很长时间一直不能生成包,有时候产生java.lang.OutOfMemoryError: GC overhead limit exceeded错误。而且编译打包时偶尔会报Error:java.lang.OutOfMemoryError

解决(在 gradle.properties中添加):

# 编译时使用守护进程
org.gradle.daemon=true
#JVM 最大允许分配的堆内存,按需分配
org.gradle.jvmargs=-Xmx16896m -XX:MaxPermSize=4096m  -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# 使用并行编译
org.gradle.parallel=true
org.gradle.configureondemand=true
#启用新一代Dex编译器D8
android.enableD8=true
#启用gradle缓存
org.gradle.caching=true

运行时报警告:

WARNING: The following project options are deprecated and have been removed:
android.enableAapt2
This property has no effect, AAPT2 is now always used.

解决方案:
移除gradle.propertiesandroid.enableAapt2=true

升级API相关问题

升级到 Android9.0遇到的问题

java.lang.VerifyError: Verifier rejected class d.w.b.f.a: java.lang.String d.w.b.f.a.a(java.lang.String) failed to verify: java.lang.String d.w.b.f.a.a(java.lang.String): [0x36] 'this' argument 'Precise Reference: org.apache.commons.codec.binary.Base64' not instance of 'Reference: org.apache.commons.codec.binary.BaseNCodec' (declaration of 'd.w.b.f.a' appears in /data/app/xxx.xxx.yyldy-naDf47h1LBlsvDDqMvd1rw==/base.apk!classes3.dex)
        at d.w.b.f.a.a(AESHelper.java:39)
        at d.w.b.b.b.a(RemoteClient.java:119)
        at b.a.b.a.b(RequestApi.java:2739)
        at b.a.i.a.k.b(IsExistIMAccount.java:58)
        at xxx.xxx.view.login.WelcomeActivity.onCreate(WelcomeActivity.java:95)
        at android.app.Activity.performCreate(Activity.java:7458)
        at android.app.Activity.performCreate(Activity.java:7448)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1286)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3409)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3614)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:86)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2199)
        at android.os.Handler.dispatchMessage(Handler.java:112)
        at android.os.Looper.loop(Looper.java:216)
        at android.app.ActivityThread.main(ActivityThread.java:7625)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:524)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:987)

解决:

public static String AESEncrypt(String plainText) {
    String encryptedValue = null;
    try {
        Key key = new SecretKeySpec(keyWord.getBytes(), "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
        byte[] byteContent = plainText.getBytes("utf-8");
        try {
            cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(_key1));
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        }
        byte[] encValue = cipher.doFinal(byteContent);
        //把上面这句注释掉,改成底下那句
        //encryptedValue = new String(new Base64().encode(encValue));
        encryptedValue = new String(Base64.encodeBase64(encValue));
    } catch (Exception e) {
        e.printStackTrace();
    }
    return encryptedValue;
}

android 9.0 升级后遇到json 解析异常,需要改成Gson 方式

遇到的类ShoppingCartFragment类

/**
 * 构建选中的id集合,用于获取购物车商品及提交结算时验证使用
 */
private String generateSelectIdList(NewShoppingCartBean bean) {
	if (bean != null) {
		ItemCartIds itemCartIds = new ItemCartIds();
		List<ItemCartIds.ItemCartId> idList = new ArrayList<>();

		for (NewShoppingCartBean.ShoppingCartBean shoppingCartBean : bean.getShoppingCartList()) {
			for (NewShoppingCartBean.ShoppingCartBean.CartActivityItemBean cartActivityItemBean : shoppingCartBean.getCartActivityItemList()) {
				for (NewShoppingCartBean.ShoppingCartBean.CartActivityItemBean.CartItemBean cartItemBean : cartActivityItemBean.getCartItemList()) {
					ItemCartIds.ItemCartId itemCartId = new ItemCartIds().new ItemCartId();
					if (cartItemBean.getIsSelected()) {
						itemCartId.setItemCartId(cartItemBean.getItemCartId());
						idList.add(itemCartId);
					}
				}
				itemCartIds.setItemCartIds(idList);
			}
		}

		Gson gson = new Gson();
		String idsStr =  gson.toJson(itemCartIds);//需要进行替换itemCartIds,改为首字母大写,否则报错
//            String idsStr = JSON.toJSONString(itemCartIds);//需要进行替换itemCartIds,改为首字母大写,否则报错
		Debug.e(idsStr);

		return ListUtils.isEmpty(itemCartIds.getItemCartIds()) ? "" : idsStr.contains("itemCartIds") ? idsStr.replace("itemCartIds", "ItemCartIds") : idsStr;
	} else {
		return "";
	}
}

Cleartext HTTP traffic to xxx not permitted

问题:升级到Android9 出现“Cleartext HTTP traffic to xxx not permitted”

针对下一代 Android 系统(Android P) 的应用程序,将要求默认使用加密连接。解决方法有:

  • APP改用https请求

  • targetSdkVersion 降到27以下

  • 最终暴力解决

    1. 在app的res/xml下新建 network_security_config.xml
    <?xml version="1.0" encoding="utf-8"?>
    <network-security-config>
        <base-config cleartextTrafficPermitted="true" />
    </network-security-config>
    
  
  2. 在AndroidManifest.xml中
  ```xml
  <application
          ...
          android:networkSecurityConfig="@xml/network_security_config">
  </application>

升级到Androidx,android.support.design.widget.CoordinatorLayout出错

Caused by: android.view.InflateException: Binary XML file line #12: Binary XML file line #2: Error inflating class android.support.design.widget.CoordinatorLayout
 Caused by: android.view.InflateException: Binary XML file line #2: Error inflating class android.support.design.widget.CoordinatorLayout
 Caused by: java.lang.reflect.InvocationTargetException
    ...
    at androidx.appcompat.app.AppCompatDelegateImpl.setContentView(AppCompatDelegateImpl.java:469)

解决:

  1. 如果布局包含:<android.support.design.widget.CoordinatorLayout
    用以下内容代替: <androidx.coordinatorlayout.widget.CoordinatorLayout

  2. 如果布局包含:<android.support.design.widget.FloatingActionButton
    用以下内容代替<com.google.android.material.floatingactionbutton.FloatingActionButton

  3. 如果布局包含:<android.support.design.widget.AppBarLayout

用以下内容代替: <com.google.android.material.appbar.AppBarLayout

  1. 如果布局包含:<android.support.v7.widget.Toolbar

用以下内容代替: <androidx.appcompat.widget.Toolbar

升级Gradle相关问题

read-only property 'outputFile'

问题:Gradle(3.3->4.4) Cannot set the value of read-only property ‘outputFile’ for ApkVariantOutputImpl_Decorated

保意项目:原来gradle3.3,build gradle 2.3.3

android{
    ...
    applicationVariants.all { variant ->
        variant.outputs.each { output ->
            def outputFile = output.outputFile
            if (outputFile != null && "release" == variant.buildType.getName()) {
                def fileName = "byguide_v${variant.mergedFlavor.versionName}_${getBuildTime("MMdd")}.apk"
                output.outputFile = new File(output.outputFile.parent, fileName)
            }
            variant.mergedFlavor.versionName = variant.mergedFlavor.versionName + "." + getBuildTime("MMdd")
            variant.mergedFlavor.versionCode = variant.mergedFlavor.versionCode * 10000 + getBuildTime("MMdd").toInteger()
        }
    }
}
def getBuildTime(String formatStr) {
    new Date().format(formatStr)
}

升级后=》gradle4.4,build gradle 3.1.3

applicationVariants.all { variant ->
    variant.outputs.all { output ->
        def outputFile = output.outputFile
        if (outputFile != null && "release" == variant.buildType.getName()) {
            def fileName = "byguide_v${variant.mergedFlavor.versionName}_${getBuildTime("MMdd")}.apk"
            outputFileName = fileName
        }
        output.versionNameOverride = variant.mergedFlavor.versionName + "." + getBuildTime("MMdd")
        output.versionCodeOverride = variant.mergedFlavor.versionCode * 10000 + getBuildTime("MMdd").toInteger()
    }
}

All flavors must now belong to a named flavor dimension

问题:Gradle(3.3->4.4) All flavors must now belong to a named flavor dimension

defaultConfig {
    //添加这一句
    flavorDimensions "versionCode"
    ...
}

Could not resolve project :AliSDK.

Gradle(3.3->4.4) AS3.1.3上面出现这个问题,换成AS3.6.2没有这个问题

unexpected element <activity> found in <manifest>

问题:Gradle(3.3->4.4) E:\...\debug\AndroidManifest.xml:107:9-109:62: AAPT: error: unexpected element <activity> found in <manifest>.

AliSDKAndroidManifest.xml

<manifest ...>

    <activity android:name="com.alibaba.wxlib.util.RequestPermissionActivity"
        tools:node="remove"/>
	<application ...></application>
</manifest>

Annotation processors must be explicitly declared now

问题:Gradle(3.3->4.4) Annotation processors must be explicitly declared now. The following dependencies on the compile classpath are found to contain annotation processor. Please add them to the annotationProcessor configuration. \- butterknife-7.0.1.jar...

各个module下的build.gradle中,都加上

android {
	...
    defaultConfig {
        ...
        // 以上显示声明annotationProcess
        javaCompileOptions { annotationProcessorOptions { includeCompileClasspath = true } }
    }
    ...
}

Unable to find method ‘…setBootClasspath’

问题:Gradle(3.3->4.4) Unable to find method ‘org.gradle.api.tasks.compile.CompileOptions.setBootClasspath(Ljava/lang/String;)V’.

Unable to find method 'org.gradle.api.tasks.compile.CompileOptions.setBootClasspath(Ljava/lang/String;)V'.
Possible causes for this unexpected error include:
Gradle's dependency cache may be corrupt (this sometimes occurs after a network connection timeout.)
Re-download dependencies and sync project (requires network)

The state of a Gradle build process (daemon) may be corrupt. Stopping all Gradle daemons may solve this problem.
Stop Gradle build processes (requires restart)

Your project may be using a third-party plugin which is not compatible with the other plugins in the project or the version of Gradle requested by the project.

In the case of corrupt Gradle processes, you can also try closing the IDE and then killing all Java processes.

出现这个问题是因为Gradle插件和Gradle版本不匹配的问题:
https://blog.csdn.net/shayubuhuifei/article/details/97775208
只需要修改Gradle对应的插件版本就好,gradle版本和插件版本对应表:
https://developer.android.google.cn/studio/releases/gradle-plugin.html#updating-plugin

其他

sending message to a Handler on dead thread

toast报这个异常。解决:在主线程创建个Handler,toast写里面,需要吐司的地方去发送消息给这个handler

setVisibility无效问题

现象:view.setVisibility(View.INVISIBLE);无效

原因一:对这个view设置了动画,且给此动画加了属性 anim.setFillAfter(true);

解决:

  • 方案一:
anim.setFillAfter(false);
view.setVisibility(View.INVISIBLE);
  • 方案二:
view.clearAnimation();
view.setVisibility(View.INVISIBLE);
  • 方案三:
view.postdelay延迟100毫秒
  • 方案四:
使用runOnUiThread的方式来实现

如果上述方案都不可行,检查Mainifest.xml,去除android:hardwareAccelerated="false"

对于其他原因,在setVisibility隐藏之后加个刷新。view.invalidate()或view.postinvalidate()。前面俩无效再试试自身requestLayout()或父容器的requestLayout()来强制页面刷新。

[android:android.content.res.Resources$NotFoundException: String resource ID #..](https://www.cnblogs.com/mybkn/archive/2012/07/09/2582958.html)

做Android应用开发的朋友有时候会遇到这样的Bug,,

android.content.res.Resources$NotFoundException: String resource ID #0x0 找不到资源文件ID #0x0

原因分析如下:

遇到这种情况,很有可能是把一个int型业务数据的 设置setText()或者类似的方法中, 这样Android系统就会主动去资源文件当中寻找, 但是它不是一个资源文件ID, 所以就会报出这个bug。 将int型业务数据,转换成String类型即可。

android.content.res.Resources$NotFoundException

Android资源不是可绘制的(颜色或路径)

Resource is not a Drawable (color or path): TypedValue{t=0x2/d=0x7f040151 a=2}
android.view.LayoutInflater.createView(LayoutInflater.java:620)

异常分析:由于将图片资源拷贝到了drawable-land-xhdpi目录下,本来应该拷贝到drawable-xhdpi目录下

解决方法:

  • 1.引用的资源ID 是否能匹配到R.java文件中定义的资源;
  • 2.是否因为缓存等原因导致编译APK时未把资源文件打包进去,可以把APK反编译检查下;
  • 3.是否使用了一个错误的类型来引用了某个资源或者配置资源时存在错误;
  • 4.是否将Int等整型变量作为了参数传给了View.setText调用,这种情况下该整型变量将被认为是一个资源ID号去资源列表中查找对应的资源,导致找不到对应资源错误;解决方法是做类型转换View.setText(String.valueOf(Int id))。

关于CompilationFailedException问题

Caused by: org.gradle.api.internal.tasks.compile.CompilationFailedException: Compilation failed; see

删除build/intermediates 然后重新编译

当时遇到这个错误只知道是编译错误,不知道为什么,也定不了问题所在,网上说的一堆也试了,都没用

日志的错误说的也不清晰,有很多时候我都是Rebuild或清理工程就解决了,这次是因为集成极光分享,集成到一半又删了,没删干净,所以出现了这个问题,处理方法是把下图的文件夹删掉,重新编译,立马定位到代码块找不到极光的包,删除该代码就好了,因为上次集成极光编译是没问题的,删了极光后还有缓存,Android studio都没检测出来。

Manifest merger failed with multiple errors

可以看到是AndroidManifest出错
打开AndroidManifest,切换到Merged Manifest,可以查看报错信息

The APK does not exist

问题:The APK file xxx.apk does not exist on disk. Error while Installing APK

variant.outputs.each { output ->
	def outputFile = output.outputFile
	if (outputFile != null && outputFile.name.endsWith('.apk')) {
	   // apk_渠道名-版本号-版本名称-编译时间.apk
	   def fileName = "xxx.apk"
	   tput.outputFile = new File(outputFile.parent, fileName)
	}
}

给apk文件重命名时发生了问题, 如果除.apk之外的文件名中还包含. 的话, 可能会出现这个问题,
好吧 将重命名格式改的简单点, 问题就解决了

编译无法成功的问题

  • 这个很诡异的错误,90%都是由于自定义控件在xml的路径写错了引起的

:app:transformResourcesWithMergeJavaResForDebug

Error:Execution failed for task ':app:transformResourcesWithMergeJavaResForDebug'. > 
Unexpected scopes found in folder 'D:\twoMoveMent\app\build\intermediates\transforms\mergeJavaRes\debug'. 
Required: PROJECT. Found: EXTERNAL_LIBRARIES, PROJECT, SUB_PROJECTS

解决方案:将bulid目录删掉 重新编译即可!!!!!少走弯路

debug无法显示值的问题

Android stdio IDE环境下debug代码不显示变量值,add to watch无效
解决方案:
修改app.gradle 文件下的testCoverageEnabled为false

buildTypes {
	debug {
	    minifyEnabled false
	    signingConfig signingConfigs.debug
	    testCoverageEnabled false
	}
}

NormalDemocompile Classpath问题

  1. Unable to resolve dependency for ':face@NormalDemo/compileClasspath': Could not resolve project :aliveDetect.
 <a href="openFile:E:/ProjectLandi/LandiTool/ScanFaceTool/face/build.gradle">Open File</a>
<br>
<a href="Unable to resolve dependency for &#39;:face@NormalDemo/compileClasspath&#39;: Could not resolve project :aliveDetect.">Show Details</a>
  1. Unable to resolve dependency for ':face@NormalDemoUnitTest/compileClasspath': Could not resolve project :aliveDetect.
 <a href="openFile:E:/ProjectLandi/LandiTool/ScanFaceTool/face/build.gradle">Open File</a>
<br>
<a href="Unable to resolve dependency for &#39;:face@NormalDemoUnitTest/compileClasspath&#39;: Could not resolve project :aliveDetect.">Show Details</a>

答案:
出现以上情况就是因为Could not resolve project :aliveDetect.这个工程下的gradle有问题,配置跟宿主工程有点不一样才会造成这样的
aliveDetectgradle下面是这样的:

buildTypes {
	      release {
	          minifyEnabled false
	          proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
	      }
	      debug {
	          minifyEnabled false
	          proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
	      }
	  }

app的gradle下面是这样的:

  buildTypes {
	      debug({
	          minifyEnabled true
	          proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
	          signingConfig signingConfigs.config
	      })
	      demo({
	          minifyEnabled true
	          proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
	          signingConfig signingConfigs.config
	      })
	      release {
	          minifyEnabled true
	          proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
	          signingConfig signingConfigs.config
	      }
	  }

注意到是因为appbuild.gradle中多了一个demo,,,,,,就是这个demo引起的要么删除 要么就给我在aliveDetect这个地方添加,这样就不会有错误了,也可以编译过去
不会造成 编译之后出现Try Again Open Messages View Show Log in Explorer.

transformClassesWithInstantRunForNormalDebug问题

java.lang.RuntimeException: com.android.build.api.transform.TransformException: java.lang.RuntimeException:Failed to find byte code for java/awt/RenderingHints$Key com.android.build.api.transform.TransformException: java.io.IOException: Failed to find byte code for java/awt/RenderingHints$Key
切记这个要一致:

compileOptions {
	    sourceCompatibility JavaVersion.VERSION_1_7
	    targetCompatibility JavaVersion.VERSION_1_7
}

java.lang.OutOfMemoryError

问题:运行项目出现java.lang.OutOfMemoryError: GC overhead limit exceeded...

解决:在项目build.gradle中添加

android {
      dexOptions {
            javaMaxHeapSize "4g"
      }
}

解决方案

该异常表示未能成功分配字节内存,通常是因为内存不足导致的内存溢出。

[解决方案]:OOM就是内存溢出,即Out of Memory。也就是说内存占有量超过了VM所分配的最大。怎么解决OOM,通常OOM都发生在需要用到大量内存的情况下(创建或解析Bitmap,分配特大的数组等),这里列举常见避免OOM的几个注意点:
1.适当调整图像大小。
2.采用合适的缓存策略。
3.采用低内存占用量的编码方式,比如Bitmap.Config.ARGB_4444比Bitmap.Config.ARGB_8888更省内存。
4.及时回收Bitmap。
5.不要在循环中创建过多的本地变量。
6.自定义对内存分配大小。
7.特殊情况可在mainfests的Application中增加 android:largeHeap="true"属性,比如临时创建多个小图片(地图marker)

Picasso导致的OutOfMemoryError 解决方案

在application中添加android:largeHead="true"

<application
             android:name=".MyApplication"
             android:largeHeap="true"
             android:icon="@mipmap/ic_launcher"
             android:label="融商会"
             android:supportsRtl="true"
             android:theme="@style/AppTheme"
             android:windowSoftInputMode="adjestPan"></application>

unable to unzip

问题:Gradle build failed - unable to unzip

You have just to do in a Terminal (for have terminal : Select View > Tool Windows > Terminal) :

On Windows:

gradlew cleanBuildCache

On Mac or Linux:

./gradlew cleanBuildCache

And build Gradle

‘:app:javaPreCompileDebug’

在app的build中

android {
    ...
    defaultConfig {
        ...
        //添加如下配置就OK了
        javaCompileOptions { annotationProcessorOptions { includeCompileClasspath = true } }
    }
    ...
}

java.lang.IllegalStateException非法状态异常

IllegalStateException: Can not perform this action after onSaveInstanceState:

onSaveInstanceState方法是在该Activity即将被销毁前调用,来保存Activity数据的,如果在保存完状态后再给它添加Fragment就会报错。

解决:

commit()换成commitAllowingStateLoss()

其他延申

  • 错误类型大致为以下几种:
java.lang.IllegalStateException:Can't change tag of fragment d{e183845 #0 d{e183845}}: was d{e183845} now d{e183845 #0 d{e183845}}
java.lang.IllegalStateException:Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 37 path $.data
  • 第一种:我在显示fragment的代码中使用了:fragment.show(getSupportFragmentManager, fragment.toString());而这里是因为两次toString()结果不同,导致不同的tag指向的是同一个fragment。获取fragment的tag的正确方法应该是使用其提供的fragment.getTag()方法。
  • 第二种:该异常是由于服务器错误返回的JSON字符串和服务器正常下时返回的JSON字符串结构不同,导致利用Gson解析的时候报了一个异常:本该去解析集合却强制去解析对象所致.解决办法:在使用Gson解析JSON时try cash一下,不报错按照正常逻辑继续解析,报异常则处理为请求失败逻辑即可.

IllegalStateException:Can’t compress a recycled bitmap

无法压缩回收位图

Can't compress a recycled bitmap
com.paidian.hwmc.utils.i.a(FileUtils.java:75)

如果位图已被回收,则希望抛出异常的方法将调用此值。知道了崩溃的具体位置,就该分析具体的原因呢!

public boolean compress(CompressFormat format, int quality, OutputStream stream) {
    checkRecycled("Can't compress a recycled bitmap");
    //省略代码
    return result;
}

//如果位图已被回收,则希望抛出异常的方法将调用此值。
private void checkRecycled(String errorMessage) {
    if (mRecycled) {
        throw new IllegalStateException(errorMessage);
    }
}

异常分析:
使用了已经被释放过内存的对象。对于BitmapBitmap bitmap=一个bitmap对象。使用过程中调用bitmap.recycle(),之后再使用bitmap就会报错。

bitmap.recycle()解释:释放与此位图关联的本机对象,并清除对象数据的引用。这将不会同步释放对象数据。它只允许在没有其他引用的情况下对其进行垃圾手机。位图被标记为“死”,这意味着如果调用getPixels()setPixels()它将抛出异常,而不会绘制任何内容。此操作不能反转,因此只有在确定没有进一步使用位图的情况下才应该调用该操作。这个一个高级调用,通常不需要调用,因为当没有对此位图的引用时,普通GC进程将释放此内存。

Free the native object associated with this bitmap, and clear the reference to the pixel data

解决方法:

  1. 在使用bitmap前增加判断if(mBitmap.isRecycled()) return null;

java.lang.IllegalArgumentException参数不匹配异常

参数不匹配异常,通常由于传递了不正确的参数导致

常见的出现场景

  • Activity、Service状态异常;
  • 非法URL;
  • UI线程操作。
  • Fragment中嵌套了子Fragment,Fragment被销毁,而内部Fragment未被销毁,所以导致再次加载时重复,在onDestroyView() 中将内部Fragment销毁即可
  • 在请求网络的回调中使用了glide.into(view),view已经被销毁会导致该错误

java.lang.NullPointerException空指针异常

解决方法:(采取不信任原则)

  1. 方法形参要判空后才使用
  2. 全局变量容易被系统回收或者更改,使用全局变量前建议判空
  3. 第三方接口的调用,对返回值进行判空
  4. 请注意线程安全

android.view.WindowManager$BadTokenException异常,Toast报错Unable to add window

android.view.WindowManager$BadTokenException
    Unable to add window -- token android.os.BinderProxy@7f652b2 is not valid; is your activity running?

异常分析:

  • 这个异常发生在Toast显示的时候,原因是因为token失效。通常情况下,一般是不会出现这种异常。但是由于在某些情况下, Android进程某个UI线程的某个消息阻塞。导致 TN 的 show 方法 post 出来 0 (显示) 消息位于该消息之后,迟迟没有执行。这时候,NotificationManager 的超时检测结束,删除了 WMS 服务中的 token 记录。删除 token 发生在 Android 进程 show 方法之前。这就导致了上面的异常。
  • 测试代码。模拟一下异常的发生场景,其实很容易,只需要这样做就可以出现上面这个问题
Toast.makeText(this,"潇湘剑雨-yc",Toast.LENGTH_SHORT).show();
try {
	Thread.sleep(20000);
} catch (InterruptedException e) {
	e.printStackTrace();
}

解决方法:

  • 第一种,既然是报is your activity running,那可以不可以在吐司之前先判断一下activity是否running呢?
  • 第二种,抛出异常增加try-catch,代码如下所示,最后仍然无法解决问题
    • 按照源码分析,异常是发生在下一个UI线程消息中,因此在上一个ui线程消息中加入try-catch是没有意义的。而且用到吐司地方这么多,这样做也不方便啦!
  • 第三种,那就是自定义类似吐司Toast的view控件。个人建议除非要求非常高,不然不要这样做。毕竟发生这种异常还是比较少见的

哪些情况会发生该问题?

  • UI 线程执行了一条非常耗时的操作,比如加载图片等等,就类似上面用 sleep 模拟情况
  • 进程退后台或者息屏了,系统为了减少电量或者某种原因,分配给进程的cpu时间减少,导致进程内的指令并不能被及时执行,这样一样会导致进程看起来”卡顿”的现象
  • 当TN抛出消息的时候,前面有大量的 UI 线程消息等待执行,而每个 UI 线程消息虽然并不卡顿,但是总和如果超过了 NotificationManager 的超时时间,还是会出现问题

"惜梦"整理的Android开发错误集锦

本文地址:https://blog.csdn.net/ptwenzi/article/details/112846226

相关标签: Android