android 6.0 以上的开发过程中的bug总结,扫荡过的坑(后续慢慢添加)

本文涵盖Android开发中常见的启动crash、PopupWindow显示、权限申请、动态权限处理、网络请求异常、安装APK问题及编译错误等,提供详细的解决策略与代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一:启动crash

android8.0一启动就crash了,在错误日志中看到了如下的一句信息;Caused by: java.lang.IllegalStateException: Only fullscreen opaque activities can request orientation,意思就是“只有不透明的全屏activity可以自主设置界面方向” ,可以去掉透明主题或者去掉设置屏幕方向

 

二:PopupWindow无法全屏显示

遇到一种现象:横向全屏界面时,底部设置了一个PopupWindow控制界面,列如播放器的播放页面,控制操作用PopupWindow写的,这时在tagetSdkVersion为21的情况下,PopupWindow可以全屏显示正常。但是当tagetSdkVersion改为28时无法全屏显示,右边的虚拟按键那部分无法覆盖。这个情况下加一个语句可以解决:

mPopupWindow.setClippingEnabled(false);但是在9.0和8.0的全面屏手机上还是有问题,横屏全屏显示时,90度方向,左边空白右边超出,270度是正常显示,可以设置反向横屏固定方向,最好的方式是用view显示不用popupwindow

 

三 :开发者模式连接手机安装驱动失败解决办法

如果电脑没有安装过驱动,可以先下载91助手然后连接手机

打开电脑  ==》属性 ==》 设备管理器 ==》 其他设备 ==》hdb黄色感叹号的,更新驱动

 

四:android studio 找不到符号 符号: 类 AndroidHttpClient

在android添加  useLibrary 'org.apache.http.legacy'  如图

android {
    compileSdkVersion 28
    buildToolsVersion "28.0.3"
    useLibrary 'org.apache.http.legacy'

    defaultConfig {
        applicationId "com.xx.xxx"
        minSdkVersion 21
        targetSdkVersion 28
        multiDexEnabled true
    }

    buildTypes {
        release {
            minifyEnabled false   //true表示混淆
//            proguardFiles 'proguard.cfg'
        }
    }
    lintOptions {
        checkReleaseBuilds false
        abortOnError false
    }
}

同时在Androidmanifest文件

<application

        <uses-library android:name="org.apache.http.legacy" android:required="false" />

</application>

五:Android 9 请求网络报异常:Cleartext HTTP traffic not permitted

<application
    android:name="com.xx.xxxx.MyApplication"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:usesCleartextTraffic="true"
    android:theme="@style/noTitleTheme">

      .......

</application>

 

六:多种动态权限申请

// 声明一个集合,在后面的代码中用来存储用户拒绝授权的权
	private final int MAIN_PERMISS_CODE = 13;
	private List<String> mPermissionList = new ArrayList<>();
	private String[] permissions = new String[]{
			Manifest.permission.WRITE_EXTERNAL_STORAGE,
			Manifest.permission.READ_EXTERNAL_STORAGE,
			Manifest.permission.READ_PHONE_STATE,
			Manifest.permission.ACCESS_COARSE_LOCATION,
	};//			Manifest.permission.CAMERA,
boolean needRequestPermission(){
		if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
			return true;
		}
		return false;
	}
	private void checkPermisson(int code){
		//PermissionChecker   ActivityCompat supportV4里面已经支持不需要做系统版本验证
		if(code == MAIN_PERMISS_CODE){
			mPermissionList.clear();
			for (int i = 0; i < permissions.length; i++) {
				if (!selfPermissionGranted(WelcomeAdActivity.this,permissions[i])) {
					mPermissionList.add(permissions[i]);
				}
			}
			if (mPermissionList.isEmpty()) {//未授予的权限为空,表示都授予了
				initActivity();
			} else {//请求权限方法
				String[] permissions = mPermissionList.toArray(new String[mPermissionList.size()]);//将List转为数组
				ActivityCompat.requestPermissions(WelcomeAdActivity.this, permissions, MAIN_PERMISS_CODE);
			}
		}
	}
	public boolean selfPermissionGranted(Context context, String permission) {
		// For Android < Android M, self permissions are always granted.
		boolean result = true;

		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

			if (getTargetSdkVersion(context) >= Build.VERSION_CODES.M) {
				// targetSdkVersion >= Android M, we can
				// use Context#checkSelfPermission
				result = context.checkSelfPermission(permission)
						== PackageManager.PERMISSION_GRANTED;
			} else {
				// targetSdkVersion < Android M, we have to use PermissionChecker
				result = PermissionChecker.checkSelfPermission(context, permission)
						== PermissionChecker.PERMISSION_GRANTED;
			}
		}

		return result;
	}
	private static int getTargetSdkVersion(Context context) {
		int version = 0;
		try {
			final PackageInfo info = context.getPackageManager().getPackageInfo(
					context.getPackageName(), 0);
			version = info.applicationInfo.targetSdkVersion;
		} catch (PackageManager.NameNotFoundException e) {
			e.printStackTrace();

		}
		return version;
	}
	@Override
	public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
		switch (requestCode) {
			case MAIN_PERMISS_CODE:
				initActivity();
				break;
			default:
				break;
		}
	}

	private void initActivity(){
		//你的内容
	}

七:权限问题:

使用如下方式读取相册数据,需要android.permission.READ_EXTERNAL_STORAGE这个权限,拒绝权限且没加try catch会挂掉,然后在需要权限的代码最好最好加上try  catch,不然会直接crash

Cursor cursor = resolver.query(Media.EXTERNAL_CONTENT_URI, new String[] { ImageColumns.BUCKET_DISPLAY_NAME,
            ImageColumns.DATA, ImageColumns.DATE_ADDED, ImageColumns.SIZE }, "bucket_display_name = ?",
      new String[] { name }, ImageColumns.DATE_ADDED);

八:7.0以上的手机安装apk问题

第一步:

File apkfile = new File(m_file,saveFileName);
	    if (!apkfile.exists()) {
		return;
	    }
            Intent i = new Intent(Intent.ACTION_VIEW);
            if (Build.VERSION.SDK_INT >= 24) {
		Log.v(TAG,"7.0以上1,正在安装apk...");
		//参数1 上下文;参数2 Provider主机地址 authorities 和配置文件中保持一致 ;参数3  共享的文件
                Uri apkUri = FileProvider.getUriForFile(m_Context, ConstVar.PackageName, apkfile);
                i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                i.setDataAndType(apkUri, "application/vnd.android.package-archive");
            }else{
				Log.v(TAG,"7.0以下,正在安装apk...");
                i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                i.setDataAndType(Uri.parse("file://" + apkfile.toString()), "application/vnd.android.package-archive");
            }
            m_Context.startActivity(i);
public static final String PackageName = "com.xx.xxx.fileprovider";(com.xx.xxx是包名)

第二步:在androidmanifest文件中application节点中添加如下代码,com.xx.xxx.fileprovider必须和安装apk方法

的authorities参数保持一致
<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="com.xx.xxx.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/filepaths" />
</provider>

第三步:在res目录下创建一个xml目录,也要在xml目录下创建一个filepaths文件,该文件名称和provider定义保持一致,名称可自选,文件内容如下:

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <paths>
        <!--
        files-path:          该方式提供在应用的内部存储区的文件/子目录的文件。
                              它对应Context.getFilesDir返回的路径:eg:”/data/data/com.jph.simple/files”。

        cache-path:          该方式提供在应用的内部存储区的缓存子目录的文件。
                              它对应getCacheDir返回的路径:eg:“/data/data/com.jph.simple/cache”;

        external-path:       该方式提供在外部存储区域根目录下的文件。
                              它对应Environment.getExternalStorageDirectory返回的路径:

        external-cache-path: 该方式提供在应用的外部存储区根目录的下的文件。
                              它对应Context#getExternalFilesDir(String) Context.getExternalFilesDir(null)
                              返回的路径。eg:”/storage/emulated/0/Android/data/com.jph.simple/files”
        -->
        <external-path name="external_path" path="." />
    </paths>
</paths>

path="."  代表访问所有路径

files-path对应 content.getFileDir() 获取到的目录。
cache-path对应 content.getCacheDir() 获取到的目录
external-path对应 Environment.getExternalStorageDirectory() 指向的目录。
external-files-path对应 ContextCompat.getExternalFilesDirs() 获取到的目录。
external-cache-path对应 ContextCompat.getExternalCacheDirs() 获取到的目录

TAG_ROOT_PATH root-path  指   /
TAG_FILES_PATH files-path  指   /data/data/<包名>/files
TAG_CACHE_PATH cache-path  指    /data/data/<包名>/cache
TAG_EXTERNAL external-path 指   /storage/emulate/0
TAG_EXTERNAL_FILES external-files-path   指   /storage/emulate/0/Android/data/<包名>/files
TAG_EXTERNAL_CACHE external-cache-path   指    /storage/emulate/0/Android/data/<包名>/cache

 

 

备注:针对一加手机,在7.0以上如上方法安装apk会有问题提示解析包时出现问题,解决思路如下:报错提示如下图

E/OnePlusAppBootManager: forbid start cpi=ContentProviderInfo{name=com.huang.fwxthh.fileprovider className=android.support.v4.content.FileProvider}
 E/ActivityThread: Failed to find provider info for com.huang.fwxthh.fileprovider
W/InstallStaging: Error staging apk from content URI
    java.io.FileNotFoundException: No content provider: content://com.huang.fwxthh.fileprovider/external/xthh/xthh_1.0.apk
        at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1489)
        at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:1340)
        at android.content.ContentResolver.openInputStream(ContentResolver.java:1060)
        at com.android.packageinstaller.InstallStaging$StagingAsyncTask.doInBackground(InstallStaging.java:436)
        at com.android.packageinstaller.InstallStaging$StagingAsyncTask.doInBackground(InstallStaging.java:389)
        at android.os.AsyncTask$2.call(AsyncTask.java:333)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:764)

这是在一加9.0的手机上的日志,一看,是FileProvider被禁止访问,导致了无法获取到已经下载好的apk文件,才报解析包错误。应该是某些操作导致了FileProvider被禁止。

而我的安装apk代码如下:

private void installApk(){
		try{
			File apkfile = new File(saveFileName);
			if (!apkfile.exists()) {
				return;
			}
			Intent i = new Intent(Intent.ACTION_VIEW);
			Debugs.debug("filename = " + "file://" + apkfile.toString());
			//没有这句 安装好了,点打开,是不会打开新版应用的
			if (Build.VERSION.SDK_INT >= 24) {
				Log.v(TAG,"7.0以上1,正在安装apk...");
				Uri apkUri = FileProvider.getUriForFile(mContext, ConstVar.PackageName, apkfile);
				i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
				i.setDataAndType(apkUri, "application/vnd.android.package-archive");
			}else{
				Log.v(TAG,"7.0以下,正在安装apk...");
				i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
				i.setDataAndType(Uri.parse("file://" + apkfile.toString()), "application/vnd.android.package-archive");
			}
//        i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//        i.setDataAndType(Uri.parse("file://" + apkfile.toString()), "application/vnd.android.package-archive");
			mContext.startActivity(i);
			//没有这句 最后不会提示完成,打开
			android.os.Process.killProcess(android.os.Process.myPid());
		}catch (Exception e){
			e.printStackTrace();
		}
	}

注意下面这句代码,杀应用进程代码,可能还没开始访问FileProvider,进程被杀掉了导致被禁止访问,可以屏蔽这句代码,亲测有效

android.os.Process.killProcess(android.os.Process.myPid());

方法二解决上面的问题:修改xml文件  将external-path的标签,注意是name标签的值用 .  替代

<external-path name="." path="." />

方法二解决上面的问题仍然是修改xml文件,假如apk的安装路径在sd卡的download文件下,那么path可以命名为 /download,那么name必须和path保持一致 就是name的值为download,如下:

<external-path name="download" path="/download" />

方法二和方法三解决方法我不太清楚为什么name必须和path一致才能解决 一加手机的问题(name是别名,值是可以随意取的,但是经测试,只要安装apk之后添加android.os.Process.killProcess(android.os.Process.myPid())的代码,name如果随意取值的话,一定会报解析包错误),我的理解是,如果name是个随意的值就是和path不一致,还没开始访问FileProvider,进程被杀掉了导致被禁止访问,这时找不到apk,可能从我们的默认标签开始查找一遍,正好name和path一致,匹配到相应的apk,所以没问题。还有一个主意,如果我们的apk在二级目录安装下面,例如:/storage/emulated/0/test/download/test_1.0.apk

test是一级目录,download是二级目录,apk在二级目录的下,xml的文件可以用如下写法

<external-path name="test" path="/test" /> 或者<external-path name="test/download" path="/test/download" />

具体这样写法,可以看getUriForFile的源码,提取源码,用测试用例自己测试看下如何生成uri的,如下是我分别提取了响应的源码测试的,下面只是提供了一级目录的测试用例,其它可以自己随便提供测试,看下流程,如果path随便写,会是什么结果,都能自己测试出来,name随便写,不影响测试用例,但是部分手机会受影响如一加手机

public Uri getUriForFile(File file) {
		HashMap<String, File> mRoots = new HashMap();
		mRoots.put("download", new File("/storage/emulated/0/download"));
//		mRoots.put(".", new File("/storage/emulated/0"));

		String path;
		try {
			path = file.getCanonicalPath();
		} catch (IOException var7) {
			throw new IllegalArgumentException("Failed to resolve canonical path for " + file);
		}

		Log.e(TAG, "要安装的apk路径为33 path== " + path );

		Map.Entry<String, File> mostSpecific = null;
		Iterator var4 = mRoots.entrySet().iterator();

		while(true) {
			Map.Entry root;
			String rootPath;
			do {
				do {
				    boolean flag = !var4.hasNext();
                    Log.e(TAG, "要安装的apk路径为44 flag== " + flag );

					if (flag) {
						if (mostSpecific == null) {
							throw new IllegalArgumentException("Failed to find configured root that contains " + path);
						}

						String rootPathaa = ((File)mostSpecific.getValue()).getPath();

						Log.e(TAG, "要安装的apk路径为4 rootPathaa== " + rootPathaa );



						if (rootPathaa.endsWith("/")) {
							path = path.substring(rootPathaa.length());
						} else {
							path = path.substring(rootPathaa.length() + 1);
						}
						Log.e(TAG, "要安装的apk路径为5 path== " + path );


						path = Uri.encode((String)mostSpecific.getKey()) + '/' + Uri.encode(path, "/");

						Log.e(TAG, "要安装的apk路径为6 path " + path );

						return (new Uri.Builder()).scheme("content").authority(ConstVar.PackageName).encodedPath(path).build();
					}

					root = (Map.Entry)var4.next();
					rootPath = ((File)root.getValue()).getPath();

					Log.e(TAG, "要安装的apk路径为7  rootPath " +  rootPath);

				} while(!path.startsWith(rootPath));
				Log.e(TAG, "要安装的apk路径为9  " );

			} while(mostSpecific != null && rootPath.length() <= ((File)mostSpecific.getValue()).getPath().length());

			Log.e(TAG, "要安装的apk路径为8 mostSpecific " +  mostSpecific);

			mostSpecific = root;
		}
	}

	public File getFileForUri(Uri uri) {
		HashMap<String, File> mRoots = new HashMap();
		mRoots.put(".", new File("/storage/emulated/0/"));

		String path = uri.getEncodedPath();
		int splitIndex = path.indexOf(47, 1);
		String tag = Uri.decode(path.substring(1, splitIndex));
		path = Uri.decode(path.substring(splitIndex + 1));

        Log.e(TAG, "要安装的apk路径为 path " + path + " tag === " + tag);


		File root = (File)mRoots.get(tag);

        Log.e(TAG, "要安装的apk路径为 root " + root.getPath());


		if (root == null) {
			throw new IllegalArgumentException("Unable to find configured root for " + uri);
		} else {
			File file = new File(root, path);

			try {
				file = file.getCanonicalFile();
			} catch (IOException var8) {
				throw new IllegalArgumentException("Failed to resolve canonical path for " + file);
			}

			Log.e(TAG, "要安装的apk路径为4 =="+file.getPath() + " root === " + root.getPath());

			if (!file.getPath().startsWith(root.getPath())) {
				throw new SecurityException("Resolved path jumped beyond configured root");
			} else {
				return file;
			}

		}
	}

 

九:安装apk权限8.0以上,不加无法弹安装

<!--安卓8.0打开apk安装更新-->
 <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />,被这玩意折磨了会,安装apk之后不加这个权限,系统不弹安装界面

十:AndroidStudio出现编译错误 Received close_notify during handshake解决方法

jcenter里面的一些需要的包下载不了引起的

修改build.gradle
buildscript{
    repositories{
        //jcenter() //把这里注释掉,换成阿里的源
        maven{ url'http://maven.aliyun.com/nexus/content/groups/public/' }
        maven{ url'http://maven.aliyun.com/nexus/content/repositories/jcenter'}
        google()
 }
}
allprojects{
    repositories{
        //jcenter() //把这里注释掉,换成阿里的源
        maven{ url'http://maven.aliyun.com/nexus/content/groups/public/' }
        maven{ url'http://maven.aliyun.com/nexus/content/repositories/jcenter'}
 } 
}
 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值