Android日常开发笔记

记录Android开发过程中遇到的问题以及解决方案

1. Android SDK AsyncTask doInBackground方法不能运行的问题

突然发现AsyncTask().execute();不能执行了。

原因:由于Android不同的版本中AsyncTask的行为不一致造成的。

Donut (Android:1.6 API:4) 以前task是串行执行的,Donut 到**Gingerbread (Android:2.3 API:9)**版本之间task是并行执行的。从 **Honeycomb (Android:3.0 API:11)**开始,task又改回了串行执行,不过SDK提供了一个新的用于并行执行的方法AsyncTask().executeOnExecutor(Executor)

解决方法:根据不同的版本选择不同的执行方法

AsyncTask<Void,Void,Void> myTask = new AsyncTask<Void,Void,Void>() { ... };
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB)
       myTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
else
       myTask.execute();

2.Android 中使用图片加载库Glide的 ImageView不能设置Tag的问题

问题:使用Glide设置图片的ImageView不能设置Tag,报You must not call setTag() on a view Glide is targeting 的错误

解决方法:

在 src/main/values/目录中新建一个 ids.xml 文件,添加一条 id数据:
<item type="id" name="glide_tag" />
然后在App中的onCreate方法中

public class App extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        ViewTarget.setTagId(R.id.glide_tag);
    }
}

3.应用放置后台很久,带Fragment的Activity被系统回收,出现crash

问题:当包含fragment的Activity 由于各种原因被系统回收后,打开此页面出现奔溃问题

原因:当FragmentActivity被系统回收时,其会调用onSaveInstanceState保存Fragment对象。在系统重新创建FragmentActivity时系统会恢复保存的Fragment,但是如果我们在FragmentActivity重新执行生命周期的时候重新生成Fragment对象附加到该FragmentActivity,系统恢复的FragmentActivity失去关联,所以出错。

解决方法:

第一种:在fragment中重写onSaveInstanceState方法,不做实现,阻止系统自动保存.

    @Override
    public void onSaveInstanceState(Bundle outState)
    {
        //此重写不对此fragment做保存,防止恢复时候出现的崩溃
    }

第二种:在Activity重新创建时,移除保存的fragment 对象

    static final String FRAGMENTS_TAG = "android:support:fragments";    
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {          
        if (savedInstanceState != null) {  
            savedInstanceState.putParcelable(FRAGMENTS_TAG, null);  
        }  
        super.onCreate(savedInstanceState);  
    }  

第三种:自己保存fragment的变量,然后在恢复时候重新手动恢复

4: ViewPager 滑动灵敏度(快速翻动)调节

问题:需要达到手指轻拨页面实现翻页

解决方法:

通过查看viewpager的源码,找到如下方法。此方法在onTouchEvent事件中的MotionEvent.ACTION_UP时执行,作用是决定要翻到的页面。如if滑动距离mFlingDistance与滑动速度mMinimumVelocity共同决定了快速拨动页面是否翻页的逻辑。我们只需要调节这两个参数即可。else是当慢慢滑动页面触发翻页的逻辑,如代码所示此种情况,需要滑动页面的0.6倍以上,放手页面即可翻转。

 private int determineTargetPage(int currentPage, float pageOffset, int velocity, int deltaX) {
     int targetPage;
     if (Math.abs(deltaX) > mFlingDistance && Math.abs(velocity) > mMinimumVelocity) {
         targetPage = velocity > 0 ? currentPage : currentPage + 1;
     } else {
         final float truncator = currentPage >= mCurItem ? 0.4f : 0.6f;
         targetPage = currentPage + (int) (pageOffset + truncator);
     }

     if (mItems.size() > 0) {
         final ItemInfo firstItem = mItems.get(0);
         final ItemInfo lastItem = mItems.get(mItems.size() - 1);

         // Only let the user target pages we have items for
         targetPage = Math.max(firstItem.position, Math.min(targetPage, lastItem.position));
     }

     return targetPage;
 }

滑动距离mFlingDistance与滑动速度mMinimumVelocity是 私有字段,我们可以使用反射的方式来改变其值,这两个值都只在 void initViewPager()中设置,而这个函数是在ViewPager的构造函数中调用的。

所以我们可以定义个扩展至ViewPager的类,在构造函数中设置这两个值

public MyViewPager(Context context, AttributeSet attrs)
{
    super(context, attrs);
    final float density = context.getResources().getDisplayMetrics().density;
    try {
        //修改滑动灵敏度
        Field minVelocity = ViewPager.class.getDeclaredField("mMinimumVelocity");
        minVelocity.setAccessible(true);
        minVelocity.setInt(this,(int)(20*density));

        Field minDistance = ViewPager.class.getDeclaredField("mFlingDistance");
        minDistance.setAccessible(true);
        minDistance.setInt(this,(int)(5*density));
    } catch (Exception e) {
        e.printStackTrace();
    }
}

如果觉得灵敏度仍然不够,可以以同样的方法来设置viewpager的滑动阈值mTouchSlop

 try
 {
     Field field = ViewPager.class.getDeclaredField("mTouchSlop");
     field.setAccessible(true);
     field.setInt(this, 6);
 } catch (NoSuchFieldException e)
 {
     e.printStackTrace();
 } catch (IllegalAccessException e)
 {
     e.printStackTrace();
 } catch (Exception e)
 {
     e.printStackTrace();
 }

5: 解决 ScrollView 中的内容改变后自动滑动到了底部的问题

问题:在ScrollView 包裹的LinearLayout中动态插入了一个View后,ScrollView自动滚动到了底部,而我们希望ScrollView仍然位于顶部

解决方法:

ScrollView包裹的View设置上以下两个属性

android:focusable="true"  
android:focusableInTouchMode="true"

例如

<ScrollView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scrollbars="none" >
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:focusable="true"
        android:focusableInTouchMode="true" >
        
        ...

    </LinearLayout>
</ScrollView>

6:Android7.0及以上版本APP自动安装的问题

由于Android7.0加强了文件的安全性,所以我们需要使用到FileProvider
第一步:在AndroidManifest.xml文件中加入权限

<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

第二步:在AndroidManifest.xml文件中加入自己的FileProvider

main->res->xml目录下创建一个xml文件rc_file_path.xml,文件内容为:

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path name="rc_external_path" path="."/>
</paths>

AndroidManifest文件中加入provider

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="ss007.top.downloadwithretrofit.FileProvider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/rc_file_path" />
</provider>

第三步:安装

    public void installAPK(File file, Activity mAct) {
        String authority = "ss007.top.downloadwithretrofit.FileProvider";
        mAct.startActivity(getInstallAppIntent(file, authority, true));
    }

    private Intent getInstallAppIntent(final File file,
                                       final String authority,
                                       final boolean isNewTask) {
        if (file == null) return null;
        Intent intent = new Intent(Intent.ACTION_VIEW);
        Uri data;
        String type = "application/vnd.android.package-archive";
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
            data = Uri.fromFile(file);
        } else {
            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            data = FileProvider.getUriForFile(MainActivity.this, authority, file);
        }
        intent.setDataAndType(data, type);
        return getIntent(intent, isNewTask);
    }

    private Intent getIntent(final Intent intent, final boolean isNewTask) {
        return isNewTask ? intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) : intent;
    }

7:去除第三方库加入到App里的权限。

当我们不希望第三方库声明在AndroidMenifest.xml文件中的某个权限合并到我们App里时,可以使用

tools:node=”remove” 

压制它,如下所示

<uses-permission android:name=”android.permission.RECORD_AUDIO” tools:node=”remove” />

猜你喜欢

转载自blog.csdn.net/ShuSheng0007/article/details/107867350
今日推荐