Android 开发笔记(2)——问题整理(无法安装,程序闪退,fragment无响应等)

最近开发安卓软件预见了很多问题,也许是自己太笨,总是犯一些低级错误,且在王珊搜找答案解决问题总是很慢,有时候一个 问题卡一两天,于是特地整理了这一段时间所遇到的一些问题以及解决办法。


1.用真机调试软件

基本上我们开发软件都是要在真机上运行的,因此光用模拟器调试不够,虽然模拟器调试可以调试多个不同版本,但轮到真机时总是会出现各种各样的问题,比如闪退啊之类的。所以,这里我推荐直接用真机调试,当需要兼容Android版本的时候再用模拟器。

那么,真机调试的步骤如下:

打开手机的设置——关于手机——系统版本,连续点击七下,进入开发者模式,这时在更多设置里面会出现<开发者选项>这一栏(平常是隐藏的),点进去之后将usb调试打开,然后用数据线连接上电脑,AVD Manager即会自动识别。当然因为手机厂商的不同,开发者选项的位置是不一样的,但都大同小异。以笔者的手机为例,笔者用的是小米mix2,系统为miui,在设置的最顶端有个<我的设备>,点进去再点击<全部参数>,即可显示手机的内存等参数,然后在上点击七下即可打开开发者选项。

有的朋友可能会发现即使打开了usb调试,安装的时候也会报错,报错信息如下:

INSTALL_FAILED_USER _RESTRICTED: Install canceled by user. It is possible that this issue is resolved by uninstalling an existing version of the apk if it is present, and then re-installing.

这是因为在高版本的安卓系统里面,<开发者选项>里面都有一个“允许通过USB安装软件”设置,默认是关闭的,所以当通过AVD安装软件时会报错,解决方法即将其打开即可。

有的人也许会注意到在”允许通过USB安装软件“这一项下面有个“USB调试(安全设置)”,这一项在调试软件的时候也必须开启(如果软件需要权限的话,当然,大部分软件或多或少都需要一点),否则程序会因不能修改权限而闪退。


2.Fragmen相关问题

安卓的fragment是一个非常好用的东西,从字面上看fragment即碎片,其实相当于一个Activity,fragment类的方法跟Activity是一脉相承的。

 (1)fragment的初始化

很简单,只需在onCreantView方法里面加入你的布局文件即可,如下:

 public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
       view=inflater.inflate(R.layout.activity_speed,container,false);
       return view;
    }

view为面板对象,R.layout.xxxx即为你的自定义面板布局,注意,如果该方法里面有不止一个view对象,return返回的view对象一定要和当前面板对象一致,否则会出现界面点击无响应之类的情况,因为如果返回的不是同一个对象,界面就获取不到焦点,对于onclick事件也就无响应了。

由于fragment和Activity大同小异,所以并不用另设一个Activity类来处理面板事件,直接在fragment里面处理即可,关于fragment的事件处理主要在onActivityCreated方法中,重载这一方法即可。另:和在Activity中处理事件不同的是,fragment里面很多的方法不能直接调用,比如startService(),xxx.this等等,都需要在前面加上getActivity()。下面是一个例子:

   @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);


        bt=(Button)view.findViewById(R.id.startSpeed);
        t1=(TextView)view.findViewById(R.id.showSpeed);

        //获取传感器管理器
        AspeedManager=(SensorManager)getActivity().getSystemService(Context.SENSOR_SERVICE);
        //检测是否存在该传感器
        sensorA=AspeedManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        sensorM=AspeedManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);

        //获取GPS传感器
        speedManager=(LocationManager)getActivity().getSystemService(Context.LOCATION_SERVICE);
        speedProvider=speedManager.getProvider(LocationManager.GPS_PROVIDER);

        //接受service发出的广播,注册监听
        rec = new MyReceiver();
        getActivity().registerReceiver(rec,new IntentFilter("fromSpeedService"));

        /*开始测速监听*/
        bt.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                //获取服务对象
                Intent it= new Intent(getActivity(), SpeedService.class);
                //检测服务是否已经在运行,防止二次启动
                if(isServiceRunning())
                    Toast.makeText(getActivity(),"测速服务已在运行",Toast.LENGTH_SHORT).show();
                else{
                    getActivity().startService(it);
                    Toast.makeText(getActivity(),"已开始后台测速",Toast.LENGTH_SHORT).show();
                }

                if(speedProvider!=null){
                    if (!speedManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
                        Toast.makeText(getActivity(),"无法使用GPS定位!",Toast.LENGTH_SHORT).show();
                    } else {
                        listener2 = new speedListener();
                        String bestProvider = speedManager.getBestProvider(getLocationCriteria(), true);
                        if(ContextCompat.checkSelfPermission(getActivity(),android.Manifest.permission.ACCESS_FINE_LOCATION)== PackageManager.PERMISSION_GRANTED){
                            Location location = speedManager.getLastKnownLocation(bestProvider);
                            speedManager.requestLocationUpdates(
                                    LocationManager.GPS_PROVIDER, 1000, 1, listener2);
                        }
                        else
                            Toast.makeText(getActivity(),"请开启GPS定位权限!",Toast.LENGTH_SHORT).show();
                    }
                }
            }
        });

    }

(2)fragment添加到Activity

fragment是一定要添加到Activity中的,但是笔者在做的时候发现不管是用replace()还是add(),show()都无法将前一个fragment替换完全,前一个fragment总是有一部分还漏在外面,上网找了很多方法,总算是解决了。原因如下:

不管我们是在课外书上学习好还是网上学习也好,介绍fragment添加的方法就是那两种:

(一)在布局文件里面使用标记添加,如下

name 是这个fragment的布局来源,id用来指定这个容器。

(二)在Java代码中动态添加,如下:

        先声明fragment变量:

private Fragment currentFragment,f1,f2,f3;

进行fragment替换:

View.OnClickListener listener=new View.OnClickListener(){
        @Override
        public void onClick(View v){
            switch (v.getId()){
                case R.id.speed:
                    f1 = new Speed();
                    changeFragment(f1);
                    break;
                case R.id.news:
                    f2=new News();
                    changeFragment(f2);
                    break;
                case R.id.personCenter:
                    f3=new PersonCenter();
                    changeFragment(f3);
                    break;
                default:
                    break;
            }
        }
    };

    public void changeFragment(Fragment targetFragment){
        FragmentManager fm = getFragmentManager();
        FragmentTransaction ft = fm.beginTransaction();
        if(!targetFragment.isAdded()){
            if(currentFragment!=null)
                ft.hide(currentFragment);
            ft.add(R.id.mainFragment,targetFragment,targetFragment.getClass().getName());
        }
        else {
            ft.hide(currentFragment).show(targetFragment);
        }
        currentFragment=targetFragment;
        ft.commit();
    }

因为fragment是由事务来管理的,所以先构建fragment事务:

FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();

再由事务来进行替换、隐藏等操作,最后使用commit()方法提交事务。

这里有必要介绍一下replace()和add()、show()的区别,replace()每次都是将上一个fragment丢弃,然后生成一个新的fragment来替换原来的,相当于每次都要创建,这对于在fragment里面要有数据交流来说是不可取的,但是优点是当fragment有很多的时候可以减少很多内存,因为自始自终都只有一个fragment在前台显示。而add(),hide(),show()这一套组合就是将fragment加入等待池,不用的fragment就用hide()隐藏,要显示的fragment就让它show(),对于数据交流来说比较方便。具体的格式参照上面的代码。

按理说这两种方法都是可以的,但问题就出在这,当一个Activity的初始界面使用了(一)方法初始化一个fragment后,再用(二)方法去更新替换这个fragment就会发生替换不完全的情况,当时可是苦恼了好长时间。明明都是按照教程写的,就是会发生这种措不及防的错。解决办法就是:初始化Activity的时候,在布局文件里不要使用(一)方法,而是直接生成一个不包含任何那东西的容器即可。如下:

容器的类型最好使用FrameLayout。

(3)fragment点击事件无响应

这真是个愚蠢的问题。。。笔者的原因就是:笔者一开始建立布局文件的时候是建立的Activity,后来想把这个Activity当成一个fragment嵌入另一个Activity中,又新建了一个fragment Java文件,而关于界面的所有操作都给写在了Activity的Java文件中,但是生成并显示布局文件的是fragment,所以界面就不会对点击事件相应了。。。


 

3.关于闪退

相信很多朋友在刚刚开发的时候也会遇到各种各样程序闪退崩溃的问题,笔者这些天可谓是头发都掉了一把,程序一直闪退一直闪退,就是找不到毛病,在真机上测试的时候也没法看错误日志,可谓是苦恼啊。下面是我遇到的一些关于闪退的问题和解决办法。

(1)程序在真机上运行可以,在模拟器上运行崩溃或闪退

   在使用Android API23以上SDK版本写软件时,涉及到应用权限的使用时,程序就会崩溃,明明在MainFest里面添加了权限声明,在程序里也有权限检查,但就是闪退。原因就是Android api23 以后,关于系统权限处理的方式发生了一下变化,在这之前,以前的应用如果你申明了这个权限,安装之后,系统将不会再次询问,默认这个应用拥有这个权限,可以直接执行相关的权限功能。而现在,到了API23之后,每次系统执行这个应用,都会弹出dialog进行询问,用户是否同意执行相关的权限操作,如果拒绝,则程序一般直接崩溃!而之前虚拟机碰见的就是这个问题,在涉及到权限相关的问题时,虚拟机默认选择了拒绝,则程序崩溃!解决办法就是把目标SDk调到api23一下。

(2)TextView.setText()闪退

说起来这是一个大坑!因为之前用惯了Java,像println()之类的方法里面都是可以直接放入基本数据类型的,比如int,char,long等,所以这里也没注意setText()方法的属性,潜意识一位他是接受int整型的,结果就被拒了,程序一直崩溃,然而一直找不到错误在哪,最后还是无意中试探道这个错误,最坑爹的是,AS并不会报错,编译完全没问题,然后就把我坑了。。。所以,大家引以为戒哈。。。setText()方法里只能放String类型的字符串!

(3)无法获取权限闪退

笔者在做一个测试速度和加速度的软件,其中速度需要用到GPS权限,同(1),虽然各处都添加了权限处理,但调试的时候还是闪退。

原因1:没有开启权限。虽然设置了权限检查,理论上来说如果没有权限,是不会执行那一段代码的,应该不至于闪退,但实际情况就是闪退了。解决办法就是手动去设置里面的权限管理将相应的权限开启。

原因2:没有允许修改权限。我相信也有很多人即使开启了权限也还是闪退,鄙视着就是其一,检查来检查去,最终还是在USB调试里找到了答案。在前文里笔者有提到”USB安全“这个东西,,起始只要将这个选项开启就行,不过这个选项开启涉及到隐私,等闲的时候还是随着USB调试一起关闭的好。

                                                                                   Android 开发笔记(2)——问题整理(无法安装,程序闪退,fragment无响应等)_第1张图片



好了,这次的分享就到这告一段落,下次再会!

你可能感兴趣的:(Android开发)