一:启动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'}
}
}