Android Field

话 学习安卓要有锲而不舍的精神与浓厚的兴趣

话 学习安卓要坚持四个自信

话 要密切关注所选择教程的日期

话 Android Studio 的发展日新月异

话 学习 Android 要习惯看英文的文档与问答。

环境搭建常见问题(待完善)

绝大部分问题都是 GFW 造成的。

流量走 http,很多包不能通过 https 下载

具体实现是:AS 端口无论是 https 的还是 http 的全部填系统代理的 http 端口。

安卓常识

添加第三方库

根据文档/问答。 以添加 jsch 为例

In file …\app\build.gradle

1
2
3
4
dependencies {
    ...
    implementation 'com.jcraft:jsch:0.1.55'
}

another e.g. gradle doc \ 新版本变化

1
implementation 'me.neavo:vitamio:4.2.2'

注册登陆系统实现

安卓端实现 rtmp 点播

服务端搭建

用 java 获取链接

在 eclipse 上开发

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
package getLinkThroughSftp;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;  

import com.jcraft.jsch.Channel;  
import com.jcraft.jsch.ChannelExec;  
import com.jcraft.jsch.JSch;  
import com.jcraft.jsch.JSchException;  
import com.jcraft.jsch.Session;

public class getVideoLink {
    final private static String protocalName = "rtmp";
    final private static String IPaddress = "149.248.57.125";
    final private static int ffmpegPort = 1935;
    final private static String applicationName = "vod";

    final private static int defaultSSHport = 22;
    final private static String password = "B2$nBo7!]UkEL1@w";
    final private static String defaultUserName = "root";
    final public static String command = "ls /mnt/mp4s";

    public static int totalVideos;

    static List<String> videoNameList = new ArrayList<String>();

    private static String protocol(){
        String pro = protocalName;
        return pro;
    }

    private static String address(){
        String adr = IPaddress;
        return adr;
    }

    private static String application(){
        String app = applicationName;
        return app;
    }

    public void convertStringToList(String s) {
    	String str[] = s.split("\\s+");
    	videoNameList = Arrays.asList(str); 	
    }

    public void getVideoNameList(){
        JSch jsch = new JSch();    
        try {
        	java.util.Properties config = new java.util.Properties();
        	config.put("StrictHostKeyChecking", "no");
        	Session session = jsch.getSession(defaultUserName, IPaddress, defaultSSHport);
        	session.setPassword(password);
        	session.setConfig(config);
        	session.connect();
	    	System.out.println("Connected");

	    	// Create and connect channel.  
            Channel channel = session.openChannel("exec");  
            ((ChannelExec) channel).setCommand(command);
            channel.setInputStream(null);
	        ((ChannelExec)channel).setErrStream(System.err);
	        InputStream in=channel.getInputStream();
	        channel.connect();

	        byte[] tmp=new byte[1024];
	        while(true){
	          while(in.available()>0){
	            int i=in.read(tmp, 0, 1024);
	            if(i<0)break;
	            convertStringToList(new String(tmp, 0, i));
	          }
	          if(channel.isClosed()){
	            System.out.println("exit-status: "+channel.getExitStatus());
	            break;
	          }
	          try{Thread.sleep(1000);}catch(Exception ee){}
	        }
	        channel.disconnect();
	        session.disconnect();
	        System.out.println("DONE");

        }catch (JSchException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }

    public String getVideoName(int index) {		
    	return videoNameList.get(index);
    }

    public String jointLink(int index){
    	return protocol() + "://" + address() + ":" + ffmpegPort + "/" + application() + "/" +
                getVideoName(index);
    }
}

可以运行成功,但之后在 AS 里就不行。

安卓端的开发

之前的 Java 代码在 AS 不能运行原因是 jsch 的方法为耗时进程,作为主进程时会被阻塞,导致应用程式结束,解决方法是用 AsyncTask 将获取链接的动作作为子进程。参考相似的代码,将 onPostExecute 作为返回主线程的方法。

打算在 onPostExecute 中进行后续的将 List 中的元素一链接的方式显示在 UI 上。

根据上一个接口获得的 list 生成 xml

之前的想法:ListIntent

使用 AsyncTask 遇到的问题

结束时设置 flagOfAsync 加一个 while 来确保 videoNameList 拿到值

1
2
3
4
5
6
7
    @Override
        protected void onPostExecute(String host){
            Log.d("video","videoNameList.toString()");
            Log.d("video",videoNameList.toString());
            Toast.makeText(getVideoLink.this,"连接成功", Toast.LENGTH_LONG).show();
            flagOfAsync = true;
        }
1
2
3
4
5
6
7
while (!flagOfAsync) {
            Log.d("video", "mainThread");
            Log.d("video", videoNameList.toString());
            if(flagOfAsync){
                break;
            }
        }

一开始一直拿不到值,陷入循坏消耗资源,等待时间过长。

分析:异步,AsyncTask 滞后执行,不是并发的。

解决方法:尝试并发编程

若用 wait(),则无论是异步还是并发都异常终止。

最终的解决办法是把获取视频列表的方法作为一个线程放到前面的活动中去。

基于 MVC 模式对安卓应用程式的理解

V: View接受用户的请求,然后将请求传递给Controller

View 与部分 Activity

C: Controller进行业务逻辑处理后,通知Model去更新。

Activity

M: Model数据更新后,通知View去更新界面显示。我们针对业务模型,建立的数据结构和相关的类。

the Activity with the fresh data should feed it to do Adapter.

参考 stackoverflow 中的回答与 MVC 的定义,个人认为 Adapter 是 Model。

播放 rtmp 链接的视频

vitamio + ffmpeg Vitamio API

参考教程实现代码。

ffmpeg 出现 libavcodec.so: has text relocations 的问题,可以降低版本解决,但不符合我的信仰,官方给出的 x86 模拟器的解决方案,但内容过于陈旧,不适合现在的环境,仍然无法解决问题。

尝试更换 arm 模拟器,无法连接。。。

尝试连接 蛟骁 865 的真机,添加配置参数

in :app

1
2
3
4
5
6
7
defaultConfig {
...
    ndk {
                abiFilters "armeabi", "armeabi-v7a", "x86", "mips","arm64_v8a"
            }
...
}

add a line in gradle.properties

1
android.useDeprecatedNdk=true

然后,报错与 x86 模拟器下相同

下面从两个方向寻找解决方法

  1. 参考 Sat, Jul 16, 2016 的教程,从源码编译 x264 和 ffmpeg。

  2. 寻找非 vitamio 包,播放 rtmp 链接

After 9 days intermittently tring, ijkplayer saved me.

对照这个Activity,与前面说的教程,将相关的一串依赖全部复制粘贴进来。

考虑到 vitamio 搜到的都是远古时代的,且配置过于麻烦,不想再编译 ffmpeg 和 x264 了,就尝试下面的方法

  • Streamaxia: latest technology, need to learn build Android github project to test and integrate which Android method.

5/5/2020 saved tutorial. 在 CentOS 8 用 wget 下载 pdf 文件不全,有损坏,不知道为什么。

official tutorial directory.

遇到困难:下载了 .aar 文件并作为模块导入,但无法使用类似 import com.karumi.dexter.PermissionToken; 的函数。

  • ijkplayer

参考这篇教程,添加 implementation 后,将与播放 Rtmp 链接相关的 class,xml 复制粘贴到自己的工程中。最终成功播放。

stackoverflow

学到

Kotlin 优质博客

《第一行代码(第三版)》

整合别人的库(待尝试)

其它尝试

将所有依赖打包起来

.AppImage格式的包

之前失败

.arr .so 模块导入

Android设备的 cpu 类型(参数说明):

armeabiv-v7a: 第7代及以上的ARM处理器;

arm64-v8a: 第8代、64位ARM处理器;

armeabi: 第5代、第6代的ARM处理器;

mips:一种RISC处理器。

mips64:64位的。

x86: 平板;

x86_64:64位的平板。

Google Play sdk 版本管理

Google Play 会利用在应用清单中声明的 属性,从不符合其平台版本要求的设备上滤除您的应用。17

AVD Manager 参数

ABI: Application Binary Interface

ABI常表示两个程序模块之间的接口,且其中一个模块常为机器码级别的library或操作系统。

ABI定义了函数库的调用、应用的二进制文件(尤其是.so)如何运行在相应的系统平台上等细节。

Android目前支持以下七种ABI:armeabi、armeabi-v7a、arm64-v8a、x86、x86_64、mips、mips64。

日期-oriented-信息-filter

Google 搜索指令

1
2
3
how to build FFmpeg-Android.sh               # all in English
android studio  play rtmp stream  after:2019 # latest
avengers endgame before:2019-04-01           # previous

Chrome plugin

Finitimus

code 搜索技巧

github

搜索文件或路径中的字符

octocat in:file,path 匹配文件内容或文件路径中出现 "octocat" 的代码。

搜索文件名

AS

Ctrl + Shift + F 全局搜索某个变量

path/ 中直接找

.findViewById 中的 Id 存在

通过前一条方法寻找

string,color … 标签中,res/下.xml文件中定义的标签

xml 本身的文件名

兼容性

NDK 兼容性

NDK平台不是向后兼容(兼容过去的版本)的,而是向前兼容(兼容未来的版本)的。

NDK编译的版本应该尽量使用较低的版本,如minSdkVersion=“8”。

libxxx.so- text relocations 解决方案

放弃降低版本的方法,放弃在 Windows 下编译底层 ffmpeg 的解决方案,使用 ijkplayer 或基于 Linux 的 AS。

从 VPS 下载各种文件

解决:git clone 速度慢,中断。 设置 Xshell下载路径。

1
2
3
yum install -y lrzsz   # for CentOS
tar cvf FileName.tar DirName
sz FileName.tar
开发时的易错点(待完善)

so库的常见错误

使用高版本编译出的so库运行在低版本的平台上会出错。考虑到NDK是向前兼容的,应使用低版本编译。

so库放置的路径有误。

没有在每个生效的ABI目录下放置对应的so库。

解决方法

若某应用有armeabi和arm64-v8a两个ABI目录,armeabi目录里有a.so和b.so,但arm64-v8a只有a.so。 当ARMv8设备在安装此应用时,根据ABI优先级,首先发现arm64-v8a目录存在,并决定使用此ABI下的so库。 但arm64-v8a目录中没有b.so,于是报错。

此时的解决方案有:一,删除arm64-v8a目录;二,arm64-v8a目录的so库情况要与armeabi一致。

战略放弃

数据库同步云端 web登陆同步,配合nginx与Django 安卓端邮箱验证机制

行迈靡靡,中心摇摇

停止运行的原因,无 finish? 生命周期,理解,防止崩溃? remain to see see

java

回忆

调用非静态方法:

方法静态化

1
2
3
4
public static int fxn(int y) {
    y = 5;
    return y;
}

生成对象

1
2
Two two = new Two();
x = two.fxn(x);

调用静态方法

键值-选择数据类型(待完善)

eclipse如何在项目中添加第三方jar文件

鼠标右击你的项目的名称->Buid Path->单击Add External Archives…->选择文件

截取字符串/convert string to arraylist

实现结果

  • 注册登陆系统
  • 通过 SSH 获取服务器上的视频名称集合,生成相应的 ListView
  • 刷新按钮,获取服务器新的视频名称集合
  • CentOS 8 上搭建可产生 Rtmp 流的服务器
  • 基于 ijkplayer,点击相应的 ListView 播放视频

BUG 与局限性

  • 视频观看时退出后,部分系统声音仍在播放
  • 只支持安卓10及以上系统
  • 登录注册系统数据库未同步云端,且没有邮箱注册验证机制

可以优化的地方

  • UI
  • ListView 换效率更高的 RecyclerView,且按照日期从新到旧排列
  • CentOS 8 上搭建 ffmpeg 的过程写成自动化脚本
  • push 视频到服务器的自动化