用android和用iPhone的区别:奇葩安卓开发用Iphone后的改变

用android和用iPhone的区别,背景

  • 因为实在受不了Android的电量问题,所以决定更换机器。
  • 然后还维护着两款Android软件,一款需要通过手机IMEI计算一个注册码,另一款,需要解析一些Excel文件,然后上传的另外一个服务器。

那么问题来了

  • 因为为换机问题,平时外出只会带一台手机,那就需要Android机变为服务器,把iPhone机变为遥控器。那两个手机的通信方式又有哪些呢?

    • 通过主动拉的方式,让Android手机轮询一个网址,获取一些数据和变化,产生变化时,执行相应的Action, iPhone端只要可以操作网站端就可以驱动服务器的改变。Android因为支持闹钟,所以后台轮询查询服务端,记录相应的变化即可。
    • 被动接受的方式,通过短信,iPhone发送指令短信给Android,然后Android端去处理短信指令,执行操作。因为上面方案还要引入网站端,增加成本,所以我采用了短信控制的方式。
    • 上面把Android手机变为服务器,又带来了什么问题呢?
      • 问题一,还是电量问题,万一我可怜的Android机,又没有电的时候怎么办?为了解决这个问题,增加电量监测功能如下代码,当电量低的时候,主动发一条短信进行提醒,同时,停止一些耗电的轮询操作。
public class BatteryLevelReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(Utils.BATTERY_LOW_ACTION)) {
            Utils.stopMonitorAlarm(context.getApplicationContext());
            sendingSms(context, "186xxxxxxx", "手机电量低,请尽快给手机充电,定时监控已关闭!", "0");
        }
    }
}

其他积极的推动

  • 简化操作,把简单留给用户,用户提供IMEI给我后,通过QQ再返回给对方。略复杂。
    • 读取剪切板,采用了淘宝的方案,在onResume中,去读剪切板内容,序列号是否正确,如果正确,激活软件。
    • 直接省掉和用户交互的过程,客户端在onResume时,请求 xxx.com/imei.txt 文件,激活设备。那这样的话,就需要服务端了,下面说一说服务端的实现方案。
      • NOSQL时代,肯定不会去写复杂的DB管理系统,直接文件搞定,普通的ASP,PHP空间,一年几百块钱,然后直接通过FTP上传上去。没错,Android也是可以访问FTP的。FTP上传

public class FtpUtil {
    private FTPClient ftpClient;
    private String strIp;
    private int intPort;
    private String user;
    private String password;

    /* *
     * Ftp构造函数
     */
    public FtpUtil(String strIp, int intPort, String user, String Password) {
        this.strIp = strIp;
        this.intPort = intPort;
        this.user = user;
        this.password = Password;
        this.ftpClient = new FTPClient();
    }

    /**
     * @return 判断是否登入成功
     */
    public boolean ftpLogin() {
        boolean isLogin = false;
        FTPClientConfig ftpClientConfig = new FTPClientConfig();
        ftpClientConfig.setServerTimeZoneId(TimeZone.getDefault().getID());
        this.ftpClient.setControlEncoding("GBK");
        this.ftpClient.configure(ftpClientConfig);
        try {
            if (this.intPort > 0) {
                this.ftpClient.connect(this.strIp, this.intPort);
            } else {
                this.ftpClient.connect(this.strIp);
            }
            // FTP服务器连接回答
            int reply = this.ftpClient.getReplyCode();
            if (!FTPReply.isPositiveCompletion(reply)) {
                this.ftpClient.disconnect();
                L.e("登录FTP服务失败!");
                return isLogin;
            }
            this.ftpClient.login(this.user, this.password);
            // 设置传输协议
            this.ftpClient.enterLocalPassiveMode();
            this.ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
            L.i("恭喜" + this.user + "成功登陆FTP服务器");
            isLogin = true;
        } catch (Exception e) {
            e.printStackTrace();
            L.e(this.user + "登录FTP服务失败!" + e.getMessage());
        }
        this.ftpClient.setBufferSize(1024 * 2);
        this.ftpClient.setDataTimeout(30 * 1000);
        return isLogin;
    }

    /**
     * @退出关闭服务器链接
     */
    public void ftpLogOut() {
        if (null != this.ftpClient && this.ftpClient.isConnected()) {
            try {
                boolean reuslt = this.ftpClient.logout();// 退出FTP服务器
                if (reuslt) {
                    L.i("成功退出服务器");
                }
            } catch (IOException e) {
                e.printStackTrace();
                L.d("退出FTP服务器异常!" + e.getMessage());
            } finally {
                try {
                    this.ftpClient.disconnect();// 关闭FTP服务器的连接
                } catch (IOException e) {
                    e.printStackTrace();
                    L.d("关闭FTP服务器的连接异常!");
                }
            }
        }
    }

    /***
     * 上传Ftp文件
     *
     * @param localFile        当地文件
     * @param remoteUploadPath 上传服务器路径 - 应该以/结束
     */
    public boolean uploadFile(File localFile, String remoteUploadPath) {
        BufferedInputStream inStream = null;
        boolean success = false;
        try {
            this.ftpClient.changeWorkingDirectory(remoteUploadPath);// 改变工作路径
            inStream = new BufferedInputStream(new FileInputStream(localFile));
            L.i(localFile.getName() + "开始上传.....");
            success = this.ftpClient.storeFile(localFile.getName(), inStream);
            if (success == true) {
                L.i(localFile.getName() + "上传成功");
                return success;
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            L.e(localFile + "未找到");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (inStream != null) {
                try {
                    inStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return success;
    }

    /***
     * 下载文件
     *
     * @param remoteFileName     待下载文件名称
     * @param localDires         下载到当地那个路径下
     * @param remoteDownLoadPath remoteFileName所在的路径
     */

    public boolean downloadFile(String remoteFileName, String localDires,
                                String remoteDownLoadPath) {
        String strFilePath = localDires + remoteFileName;
        BufferedOutputStream outStream = null;
        boolean success = false;
        try {
            this.ftpClient.changeWorkingDirectory(remoteDownLoadPath);
            outStream = new BufferedOutputStream(new FileOutputStream(
                    strFilePath));
            L.i(remoteFileName + "开始下载....");
            success = this.ftpClient.retrieveFile(remoteFileName, outStream);
            if (success == true) {
                L.i(remoteFileName + "成功下载到" + strFilePath);
                return success;
            }
        } catch (Exception e) {
            e.printStackTrace();
            L.e(remoteFileName + "下载失败");
        } finally {
            if (null != outStream) {
                try {
                    outStream.flush();
                    outStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        if (success == false) {
            L.e(remoteFileName + "下载失败!!!");
        }
        return success;
    }

    /***
     * @param localDirectory      当地文件夹
     * @param remoteDirectoryPath Ftp 服务器路径 以目录"/"结束
     * @上传文件夹
     */
    public boolean uploadDirectory(String localDirectory,
                                   String remoteDirectoryPath) {
        File src = new File(localDirectory);
        try {
            remoteDirectoryPath = remoteDirectoryPath + src.getName() + "/";
            this.ftpClient.makeDirectory(remoteDirectoryPath);
            // ftpClient.listDirectories();
        } catch (IOException e) {
            e.printStackTrace();
            L.i(remoteDirectoryPath + "目录创建失败");
        }
        File[] allFile = src.listFiles();
        for (int currentFile = 0; currentFile < allFile.length; currentFile++) {
            if (!allFile[currentFile].isDirectory()) {
                String srcName = allFile[currentFile].getPath().toString();
                uploadFile(new File(srcName), remoteDirectoryPath);
            }
        }
        for (int currentFile = 0; currentFile < allFile.length; currentFile++) {
            if (allFile[currentFile].isDirectory()) {
                // 递归
                uploadDirectory(allFile[currentFile].getPath().toString(),
                        remoteDirectoryPath);
            }
        }
        return true;
    }

    /***
     * @param localDirectoryPath 本地地址
     * @param remoteDirectory    远程文件夹
     * @下载文件夹
     */
    public boolean downLoadDirectory(String localDirectoryPath, String remoteDirectory) {
        try {
            String fileName = new File(remoteDirectory).getName();
            localDirectoryPath = localDirectoryPath + fileName + "//";
            new File(localDirectoryPath).mkdirs();
            FTPFile[] allFile = this.ftpClient.listFiles(remoteDirectory);
            for (int currentFile = 0; currentFile < allFile.length; currentFile++) {
                if (!allFile[currentFile].isDirectory()) {
                    downloadFile(allFile[currentFile].getName(), localDirectoryPath, remoteDirectory);
                }
            }
            for (int currentFile = 0; currentFile < allFile.length; currentFile++) {
                if (allFile[currentFile].isDirectory()) {
                    String strremoteDirectoryPath = remoteDirectory + "/" + allFile[currentFile].getName();
                    downLoadDirectory(localDirectoryPath, strremoteDirectoryPath);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
            L.i("下载文件夹失败");
            return false;
        }
        return true;
    }

    // FtpClient的Set 和 Get 函数
    public FTPClient getFtpClient() {
        return ftpClient;
    }

    public void setFtpClient(FTPClient ftpClient) {
        this.ftpClient = ftpClient;
    }

    public static void main(String[] args) throws IOException {
//        Ftp ftp = new Ftp("10.3.15.1", 21, "ghips", "ghipsteam");
//        ftp.ftpLogin();
//        //上传文件夹
//        ftp.uploadDirectory("d://DataProtemp", "/home/data/");
//        //下载文件夹
//        ftp.downLoadDirectory("d://tmp//", "/home/data/DataProtemp");
//        ftp.ftpLogOut();
    }
}
  • 但这些共用的小空间,存在不稳定的可能,自己搭建服务器,太弱了,稳定压倒一切的原则下,再寻求更优的方案:OSS,阿里云存储, 现在都流行应用上云,阿里云的稳定性,比空间类的要稳定不少吧?JAVA语言的易用性,所以也是可以通过SDK直接上传文件的。

public class AliYunOSSUtils {

    /**
     * 整个文件夹上传文件
     *
     * @param endpoint
     * @param baseFilePath
     * @param subFilePath
     * @param bucket
     */
    public static void uploadDirectory(String endpoint, String baseFilePath, String subFilePath, String bucket) {
        L.d("开始上传:" + baseFilePath + File.separator + subFilePath);
        File file = new File(baseFilePath + File.separator + subFilePath);
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (int i = 0; i < files.length; i++) {
                File childFile = files[i];
                String targetFile = subFilePath + File.separator + childFile.getName();
                uploadFile(endpoint, childFile.getAbsolutePath(), targetFile, bucket);
            }
        }
    }

    /**
     * 单个上传文件
     *
     * @param endpoint
     * @param uploadFilePath
     * @param remoteFilePath
     * @param bucket
     */
    public static void uploadFile(String endpoint, String uploadFilePath, final String remoteFilePath, final String bucket) {
        // 明文设置secret的方式建议只在测试时使用,更多鉴权模式请参考后面的`访问控制`章节
        OSSCredentialProvider credentialProvider = new OSSPlainTextAKSKCredentialProvider("xxx", "xxx");
        ;
        OSS oss = new OSSClient(SmsApp.getApp(), endpoint, credentialProvider);

        // 构造上传请求
        PutObjectRequest put = new PutObjectRequest(bucket, remoteFilePath, uploadFilePath);

        // 异步上传时可以设置进度回调
        put.setProgressCallback(new OSSProgressCallback<PutObjectRequest>() {
            @Override
            public void onProgress(PutObjectRequest request, long currentSize, long totalSize) {
                // L.d("PutObject", "进度: 当前大小: " + currentSize + " 总大小: " + totalSize);
            }
        });

        OSSAsyncTask task = oss.asyncPutObject(put, new OSSCompletedCallback<PutObjectRequest, PutObjectResult>() {
            @Override
            public void onSuccess(PutObjectRequest request, PutObjectResult result) {
                L.d(bucket + " 下: " + remoteFilePath + " 上传成功");
            }

            @Override
            public void onFailure(PutObjectRequest request, ClientException clientExcepion, ServiceException serviceException) {
                // 请求异常
                if (clientExcepion != null) {
                    // 本地异常如网络异常等
                    clientExcepion.printStackTrace();
                }
                if (serviceException != null) {
                    // 服务异常
                    L.e("ErrorCode", serviceException.getErrorCode());
                    L.e("RequestId", serviceException.getRequestId());
                    L.e("HostId", serviceException.getHostId());
                    L.e("RawMessage", serviceException.getRawMessage());
                }
            }
        });
    }
}

通过上面的一系列优化后,形成如下序列图:
微信公众号

所以,一个奇葩Android开发,换了Iphone后,就产生了这些在Android应用上的想法,如果你也有类似的经历,也欢迎提出你玩出了哪些新的花样。

二、让你的应用更酷炫

  • 开源库:Lottie,实现一系列的牛X动画。如下图:
    牛X动画1

牛X动画2

  • 下面说一下接入方式:

增加依赖:


dependencies {  
  compile 'com.airbnb.android:lottie:2.0.0-beta4'
}

在Asset中放好 json文件。然后最简单的使用如下:

<com.airbnb.lottie.LottieAnimationView
        android:id="@+id/animation_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:lottie_fileName="hello-world.json"
        app:lottie_loop="true"
        app:lottie_autoPlay="true" />

至此,基本的应用就算完成了,但这些东西,你会觉得太简单了,能不能举个实际应用粟子?我看到了,但接入的过程中,还是费了点力气。
要解决的问题如下:

  1. 怎么控制动画的大小?
  2. 能不能控制一下播放完成时的一些操作?

针对第一个问题,我一开始设计了各种wrap_content, fill_parent,都未生效,然后想到去调整json文件,因为里面有w 和 h 两个属性。然而,也没有作用。千思万解之际,我去http://www.lottiefiles.com/ 下载了一些其他的模板动画文件,发现这里的动画又太大了,又有缩小的需求,妹的,看了LottieAnimationView 继承自ImageView,然后设置scaleType = fitCenter,也无效。最后想到Demo中的 iv_lav_like.setScale(0.4f); 方法。一试,果然有效,所以关于动画的大小,可以通过如下代码,需要延迟设置才会生效。

handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                iv_lav_like.setScale(0.4f);
                iv_lav_share.setScale(0.4f);
            }
        }, 1000);

第二个问题,动画播放前和播放完成后的一些Action。在结束时,把自己的动画UI给隐藏掉。


iv_lav_like = (LottieAnimationView) v.findViewById(R.id.iv_lav_like);
        iv_lav_like.setAnimation("heart.json");
        iv_lav_like.loop(false);
        iv_lav_like.setVisibility(View.GONE);
        iv_lav_like.addAnimatorListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {

            }

            @Override
            public void onAnimationEnd(Animator animation) {
                iv_lav_like.setVisibility(View.GONE);
            }

            @Override
            public void onAnimationCancel(Animator animation) {

            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });
        iv_lav_like.playAnimation();

这样就可以解决大部分的需求了。

最后,分享给爱学习的你一个188元的红包,每天只需要0.48元即可加入一个创建五年的圈子,10位嘉宾都是多年大厂背景:阿里、头条、美团、亚马逊、得物,职位从运营、产品、客户端、大前端都有,期待你的加入,一起学习交流:

微信扫码领188元立减券
微信扫码领188元立减券

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注