把日期天气组件开发完了,锁屏界面的内容基本上出来了。联系人和APP应用组件就是个RecyclerView,不再赘述。

本篇讲述APP怎么通过短信实现远程配置与服务启动。

个人觉得这个办法还是挺好用的,不用后台,也不用用户系统,可以实现远程控制。

先看看实现逻辑:

android 手游开发入门 手机端安卓开发_android


图中第六步是待实现的,1-5步都已在工程里已实现。

下面是具体实现:

1:需要短信的收发权限,要在Manifest.xml中申明:

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

权限是首要的,而且在APP中发送短信的地方还要动态获取权限。

2:将配置Model通过Gson库转化成json字符串,塞到短信发送器中:
以日期天气组件的远程配置为例:

private void sendSMS() {
        String phone = etPhone.getText().toString();
        if(StringUtils.isEmpty(phone)){
            ToastUtils.showToast(this,"请填写手机号");
            return;
        }
        if(!StringUtils.isPhoneNumber(phone)){
            ToastUtils.showToast(this,"请填写正确的手机号");
            return;
        }
        SharedUtils.getInstance().putString(SHARED_SEND_SMS_PHONE,phone);
        SmsManager manager = SmsManager.getDefault();
        switch (type){
            case SEND_WEATHER:
                manager.sendTextMessage(phone,null,getWeatherStr(),null,null);
                break;
            case SEND_APP:
                manager.sendTextMessage(phone,null,getAppStr(),null,null);
                break;
            case SEND_CONTACT:
                manager.sendTextMessage(phone,null,getContactStr(),null,null);
                break;
            case SEND_ALL:
                manager.sendTextMessage(phone,null,getWeatherStr(),null,null);
                manager.sendTextMessage(phone,null,getAppStr(),null,null);
                manager.sendTextMessage(phone,null,getContactStr(),null,null);
                break;
            case SEND_CMD:
                manager.sendTextMessage(phone,null,getCMDStr(),null,null);
                break;
        }
        ToastUtils.showToast(this,"发送成功");
        finish();
    }

    private String getWeatherStr(){
        StringBuffer smsBuffer = new StringBuffer();
        smsBuffer.append(LockerService.SMS_FLAG);
        smsBuffer.append(SmsReceiver.SMS_TYPE_WEATHER);
        WeatherConfig weatherConfig = SharedUtils.getInstance().getWeatherConfig();
        smsBuffer.append(new Gson().toJson(weatherConfig));
        return smsBuffer.toString();
    }

3:注册短信拦截接收器,使用全局广播:

<receiver
            android:name=".receiver.SmsReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter android:priority="1000">
                <action android:name="android.provider.Telephony.SMS_RECEIVED"/>
            </intent-filter>
        </receiver>

4:申明SmsReceiver类:

public class SmsReceiver extends BroadcastReceiver {

    public static final String SMS_FLAG = "[easycall]";

    public static final String SMS_TYPE_WEATHER = "[weather]";
    public static final String SMS_TYPE_APP = "[app]";
    public static final String SMS_TYPE_CONTACT = "[contact]";
    public static final String SMS_TYPE_CMD = "[cmd]";

    private static final int RECEIVERED_MSG = 1;

    Context context;


    @Override
    public void onReceive(Context context, Intent intent) {
        this.context = context;
        dealMessage(intent);


    }

    void dealMessage(Intent intent){
        Object[] object=(Object[]) intent.getExtras().get("pdus");
        SMSModel model = new SMSModel();
        for (Object pdus : object) {
            byte[] pdusMsg=(byte[]) pdus;
            SmsMessage sms= SmsMessage.createFromPdu(pdusMsg);
            String mobile=sms.getOriginatingAddress();//发送短信的手机号
            String content=sms.getMessageBody();//短信内容
            model.setPhone(mobile);
            model.setContent(content);
            break;

        }
        Message msg=new Message();
        msg.what = RECEIVERED_MSG;
        msg.obj = model;
        handler.sendMessage(msg);
    }

    private Handler handler = new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            SMSModel model = (SMSModel) msg.obj;
            String phone = model.getPhone();
            String content = model.getContent();
            PhoneModel bindPhoneModel = SharedUtils.getInstance().getPhoneModel();
            if(bindPhoneModel == null){
                return;
            }
            String bindPhone = bindPhoneModel.getPhone();
            if(StringUtils.isEmpty(bindPhone)){
                return;
            }
            bindPhone = bindPhone.replace(" ", "");// 去掉空格
            bindPhone = bindPhone.replace("+86","");// 去掉+86
            // 去掉86
            phone = phone.replace("+86","");
            if(phone.startsWith("86")){
                phone = phone.substring(2);
            }
            //只处理绑定手机发来的短信
            if(bindPhone.contains(phone)){
                if(!StringUtils.isEmpty(content) && content.startsWith(SMS_FLAG)){
                    // 配置文件
                    String configStr = content.replace(SMS_FLAG,"");
                    if(configStr.contains(SMS_TYPE_WEATHER)){
                        configStr = configStr.replace(SMS_TYPE_WEATHER,"");
                        WeatherConfig config = new Gson().fromJson(configStr,WeatherConfig.class);
                        SharedUtils.getInstance().setWeatherConfig(config);
                    } else if(configStr.contains(SMS_TYPE_APP)){
                        configStr = configStr.replace(SMS_TYPE_APP,"");
                        AppConfig config = new Gson().fromJson(configStr,AppConfig.class);
                        SharedUtils.getInstance().setAppConfig(config);
                    } else if(configStr.contains(SMS_TYPE_CONTACT)){
                        configStr = configStr.replace(SMS_TYPE_CONTACT,"");
                        ContactConfig config = new Gson().fromJson(configStr,ContactConfig.class);
                        SharedUtils.getInstance().setContactConfig(config);
                    } else if(configStr.contains(SMS_TYPE_CMD)){
                        configStr = configStr.replace(SMS_TYPE_CMD,"");
                        // 启动服务
                        if(CMDModel.CMD_START_SERVICE.equals(configStr)){
                            // 远程启动关闭时不做启动
                            if(!SharedUtils.getInstance().getRemoteStart()){
                                return;
                            }
                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                                context.startForegroundService(new Intent(context, LockerService.class));
                            } else {
                                context.startService(new Intent(context, LockerService.class));
                            }
                        }
                    }
                }
            }
        }
    };
}

这里把整个SmsReceiver类贴出来了,里面包含了各类配置文件解析,服务启动的处理。

使用短信通信有一些弊端:

1:会产生垃圾短信,虽然现在手机的短信功能基本是一个通知作用,很少再有用户用短信来做社交。这个后期可以通过清理功能来处理;
2:目前是明文短信,配置文件直接转成json字符串裸露。后期可以通过加密方式处理;
3:发送短信时系统会有个弹出APP正在使用产生资费的功能提示框需要用户确认,这个不太好处理。因此上图逻辑中的第六步暂时没实现,在配置成功发反馈短信时需要老人做这个确认不太可能。