我们先看看Android自带的视频播放器,VideoView就是AndroidAPI中的自带视频播放器,其设计有以下几点:
所以如果我们不需要自定义视图,且功能非常的简单,我们就直接可以使用VideoView播放器
在VideoView中,需要明白的是SurfaceView,MediaController和MediaPlayer各个标注位,SurfaceView是承接MediaPlayer的视图,MediaController是控制MediaPlayer的视图
所以我们自定义视频播放器的话,也可以按照这个思路来,播放器继承SurfaceView,在写一个控制视图。其实我在想为什么不设计一个小的库,然后我们直接写一个布局传进去,然后就能得到我们想要的播放的样式,这个难点
就是我们传进去的视图组件需要绑定监听啊,所以这点还是我现在无法想到解决办法的,所以我们可以实现一个播放视图,然后自己实现一个控制视图去控制它,所以我们只要把这个思路搞清楚其实就是码代码的事情和对一些逻辑
标志位的处理就行了,还有就是对Android各个自定义系统的适配问题了,因为视频这个问题很多坑。
这里先整理一下这个视频的整个逻辑
先要知道的是几个逻辑点:
以上大概就是视频播放的一些必要点,虽然看着这个逻辑不是很多,但是要把这些点串起来,并且去适配各种不同的平台和API还是有很多东西需要考虑的
上面这张图就是一个网络视频播放器的简单流程图。
META-INF/
:包含CERT.SF
和CERT.RSA
签名文件,还包括MANIFEST.MF
的文件清单
assets/
:直接打包进APK的资源,不会直接在R.java
生成资源ID,可使用AssetManager来对它进行引用使用
res/
:包含不编译成resources.arsc
的资源
lib/
:包含不同处理器的编译代码,包含各个平台类型,比如armeabi, armeabi-v7a, arm64-v8a, x86, x86_64, and mips.
APK还包含以下几个部分,除了AndroidManifest.xml
是必须的,其他都是不强制的
resources.arsc
:包含所有配置的XML内容来自res/values/
文件夹,通过工具获取XML内容,然后编译二进制文件,然后归档内容,内容包括字符串和样式,但是不包括内容的路径,例如布局文件和图片
classes.dex
:包含通过Dalvik/ART虚拟机编译java源码生成的DEX文件
AndroidManifest.xml
:包含核心的Android清单文件,此文件包含应用的名称,权限,版本,引用的库。此文件是二进制文件
APK的大小会影响APP的性能,如加载速度,内存的使用,以及电力的消耗。一个很常见的减少APK大小的方法就是减少资源文件的数量和大小,比如,你可以移除不在使用的资源,使用可以扩展的Drawable对象来代替图像文件,所以接下来就是讨论怎样减少资源文件的数量和大小
lint工具是AS中自带的静态代码分析器,检测res/
文件下没有被引用的资源,当检测发现了没有被引用的资源后会输出像以下的警告。
1 | res/layout/preferences.xml: Warning: The resource R.layout.preferences appears |
但是Lint工具无法扫描检测assets/
文件夹,因为此文件夹下的是通过反射引用的资源或者是链接的库文件,所以它是不可以的移除的资源,它只会存在一个提醒Libraries
代码库中也可能存在不被使用的情况,Gradle
可以自动的移除这些无用的资源当你在build.gradle
文件中配置shrinkResources。
1 | android { |
使用shrinkResources,就必须使用代码压缩,在构建的过程中,ProGuard会首先移除未使用的代码但不会移除未使用的资源,之后Gradle
会移除未使用的资源,(关于ProGuard和其他方法的来压缩代码和资源可学习此文章Shrink Your Code and Resources)
drawable-nodpi/
来节省空间,我们建议至少存在一个xxhdpi
的图片变种Drawable
对象比如可以对图像的变化包含一个单独的资源,在图片的旋转,切片等,建议使用同一组资源,然后在使用的时候定制它们。Android提供工具来改变资源的颜色,在5.0以上,可以是使用android:tint
和tintMode
,而低版本可以使用ColorFilter类来实现。还可以通过旋转图片来达到资源的复用,比如下面的代码就是通过旋转180度,来达到『拇指向上』到『拇指向下』的转变
1 | <?xml version="1.0" encoding="utf-8"?> |
aapt
在构建过程中可以优化和无损压缩放置在res/drawable/
的图片资源,例如,aapt
可以将不需要超过256色的真彩色PNG转换为具有调色板的8位PNG,这样可以相同质量的图片占更小的内存。但是aapt
有以下几点局限:
asset/
文件下的PNG文件aapt
优化图片文件需要256或者更少色aapt
可能会填充已经压缩的PNG文件,为了防止个问题,我们应该在Gradle
配置中使用cruncherEnabled
标志来防止此问题
1 | aaptOptions { |
WebP
文件格式可以使用WebP
格式来代替PNG和JPEG格式,当targeting超过3.2(API 13),WebP
可以提供有损压缩(JPEG)以及透明度(PNG)并且压缩效果要比PNG和JPEG效果好,所以可以使用WebP
来代替BMP, JPG, PNG 和静态GIF文件在AS中,更多信息关于WebP
可以看 Create WebP Images Using Android Studio
Google Play 只接受启动图标为PNG格式的APK
@IntDef
注释和ProGuar处理枚举代码转换为interger值,这样可以保证枚举类型的安全效益。arm-eabi-strip
工具,用来去除native库中不必要的debug符号,在这之后在打release包。.so
文件在APK存储中是不能够被压缩的,在android:extractNativeLibs
标记为false可以避免PackManager在应用安装时复制.so
文件到文件系统并且还可以使应用程序增量更新更小hdpi
的设备,只需要hdpi
资源,而不需要xxxxhdpi
资源,所以就是不需要的资源,更多的信息可以查看Configure APK Splits和Maintaining Multiple APKsyield():让步,让不低于自己优先级的线程有机会运行。(不会释放获取的锁)
interrupt():终止哪些调用中断方法进入阻塞状态的线程。(sleep,wait,join都是中断方法)
join(): 当前线程对一个线程调用此方法,当前线程就会停止运行,直到另一个线程执行完成后,才会继续执行当前线程。(会释放获得的锁,join方法内部调用了wait方法)
wait():此方法是object的实例方法,让当前线程进入阻塞状态。(释放获取对象的内部锁)可通过notify和notifyall来唤醒
notify()/notifyall():是Object的实例方法,notify()是唤醒等待该对象的线程,而notifall()是唤醒所有等待的对象。
其实现方法与复用的ListView基本相似,但在实例化ViewHolder时,我在RecyclerView里使用的是单例模式,而在ListView直接判View是否为null,设置Tag来实现,但都是只创建一个ViewHolder
RecyclerView代码
1 | public abstract class RecycleryAdapter<T> extends RecyclerView.Adapter { |
ViewHolder代码
1 | public class RecyclerViewHolder extends RecyclerView.ViewHolder{ |
MainActivity代码
1 | public class MainActivity extends AppCompatActivity { |
其实基本上和ListView中的实现是差不多,区别就是ViewHolder的实现,单例模式,需要理解的就是ViewHolder中的代码
]]>基本数据类型
boolean
基本数据开辟空间在正常情况下都是在创建在栈内,并且基本数据类型有有一个特点,比如
1 | int a=1; |
a和b引用的其实是同一块内存,值为1的内存,当在创建一个int c=1,c也会指向同一块内存,当
a=2时,将会在java内存重新开辟一块值为2的内存,记住基本类型内存时开辟在Java的栈中的,
栈不止有方法的栈入栈出,还会包含基本类型的数值。
引用数据类型
final
这个关键字,就是在开始的时候就把这个字存储在了常量池中,并且不可改变,所以是一个常量,其实在Java中,当方法添加这个关键字后,java就会将这个方法优化,在编译时,将此方法内嵌到调用了此方法中的方法中,比如
1 | final fun(int a){ |
变成
1 | f(){ |
这是Java中的一个优化,叫内嵌调用,记住当数据添加了final关键字后就会直接放在常量池,并且初始化。这里还有一点一个class对象中的基本数据都会被初始化,而方法中的不会,方法中的数据会在运行时,在运行时常量池中进行初始化,并且使用。
###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
103
104
105
106
107
108
109
110
111
112
113
114
115package com.example.lenovo.touchscreen;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private GestureDetector mGestureDetector = new GestureDetector(new MyGesture());
public boolean onTouchEvent(MotionEvent event) {
return mGestureDetector.onTouchEvent(event);
}
private TextView textView;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mGestureDetector.setIsLongpressEnabled(true);
}
class MyGesture extends GestureDetector.SimpleOnGestureListener{
public boolean onDown(MotionEvent ev) {
Log.d("onDown", ev.toString());
Toast.makeText(MainActivity.this, "onDown", Toast.LENGTH_SHORT)
.show();
return true;
}
/**
滚动
e1按上屏幕的起始位置,e2为离开屏幕的结束位置
distanceX是X轴起点与终点的水平距离
distanceY是Y轴起点与终点的水平距离
*/
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
Log.d("onScroll", e1.toString());
Toast.makeText(MainActivity.this, "onScroll", Toast.LENGTH_SHORT)
.show();
if(distanceX-distanceY<=10||distanceX-distanceY>=-10) System.exit(0);
return true;
}
/**
长按
*/
public void onLongPress(MotionEvent ev) {
Log.d("onLongPress", ev.toString());
Toast.makeText(MainActivity.this, "onLongPress", Toast.LENGTH_SHORT)
.show();
}
/**
抬起
*/
public boolean onSingleTapUp(MotionEvent ev) {
Log.d("onSingleTapUp", ev.toString());
Toast.makeText(MainActivity.this, "onSingleTapUp", Toast.LENGTH_SHORT)
.show();
return true;
}
/**
按住,比长按要短
*/
public void onShowPress(MotionEvent ev) {
Log.d("onShowPress ", ev.toString());
Toast.makeText(MainActivity.this, "onShowPress", Toast.LENGTH_SHORT)
.show();
}
/**
抛
e1按上屏幕的起始位置,e2为当前屏幕的位置
velocityX每秒X轴上移动的像素
velocityY每秒Y轴上移动的像素
这2个数据为速率
*/
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
Log.d("onFling e1 ",e1.toString());
Log.d("onFling e2 ",e2.toString());
Toast.makeText(MainActivity.this, "onFling", Toast.LENGTH_SHORT)
.show();
return true;
}
/**
双击,连续
*/
public boolean onDoubleTap(MotionEvent e) {
Log.d("onDoubleTap",e.toString());
Toast.makeText(MainActivity.this, "onDoubleTap", Toast.LENGTH_SHORT)
.show();
// System.exit(0);
return super.onDoubleTap(e);
}
/**
抬起确认,手离开屏幕
*/
public boolean onSingleTapConfirmed(MotionEvent e) {
Log.d("onSingleTapConfirmed",e.toString());
Toast.makeText(MainActivity.this, "onSingleTapConfirmed", Toast.LENGTH_SHORT)
.show();
return super.onSingleTapConfirmed(e);
}
}
}
NDK中文为原生开发工具包,从名字就可以看出‘原生’二字,也就是在Android应用中利用C和C++代码的工具,即将C或C++代码嵌入Android代码中
可以用自己的源代码构建,也可以用现有的预构建库。以下几种值得使用,除此情况都不提倡使用:
在src目录下创建一个NdkJniUtils.java(名字可以自己命名)代码文件,代码如下:
1 | package com.example.lenovo.ndkone; |
编译NdkJniUtils.java生成.h文件
1 | javah -jni com.example.lenovo.ndkone.NdkJniUtils |
在debug根目录下会生成一个名字超长的.h文件
若使用网上的另一种 javah -d jni -classpath E:/Android/sdk/platforms/android-22/android.jar;../../build/intermediates/classes/debug example.daosong.com.ndkdemo.NDK 可能会出现找不到.class文件的情况
在src/main目录下创建一个JNI Folder,将生成的.h文件复制到此文件夹中,然后创建一个.c代码文件,.c代码如下
1 |
|
接着创建一个Android.mk文件,文件内容为
1 |
|
在app 中的build.gradle中找到defaultConfig{} ,然后在里面添加
1 | ndk { |
在gradle.properties 添加 android.useDeprecatedNdk=true 否则会出现文件中有c++文件而无法被编译,建议使用Cmake或者ndk-build
的错误信息
完成以上就成功,即能够运行
今天在实现复用Adapter的时候,我在使用AddView添加组件的时候,发现当界面上下拉动的时候,添加组件会每次增加一个,并且第一部分区域和最后一部分会重复绘制多次
如图:
然后这是最尴尬的,我不知道它为什么会重复绘制,如果是刷新的话好理解,那为什么第一个和最后一个一开始就重复绘制了如此多的次数
以下部分来自http://www.cnblogs.com/nailperry/p/4675599.html
1 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { |
1 | final int measureHeightOfChildren(int widthMeasureSpec, int startPosition, int endPosition, |
一个父视图可能多次调用子视图的measure方法,意思就是说,当ListView的OnMeasure方法计算完Item的大小,ListView的父视图将要查看此结果,然后进行判定这个结果是否能被接受,若不能,则会再次调用measureHeightOfChildren方法,从而导致getView方法的重复调用,而造成第一行和最后一行被多次绘制
但是,那个上下拉后,隐藏的Item重现是再次被绘制还不是不是太清楚,有几张图,然后看了网上的ListView源码解释,还是很模糊,在此先贴图
上图意思很容易看懂,但是我还是无法理解重复绘制的问题,我在代码中添加的组件怎么会在重回屏幕时增加
对于上面的问题,我在前几天突然豁然开朗,就是ListView中的item放进RecycleBin后,在下拉后会复用RecycleBin中的item,所以在item加一个view后,在复用中,此view还是会存在于此item中,我们每次下拉或者刷新,都只是更改item中的信息,或者图片,并没有添加过view或者其他一些组件,所以当你有item的添加组件操作,会在下面的刷新中,复用此item中的view越来越多,因为我们是在getView()方法中进行信息或者图片加载,而每次下拉总是会调用getView(),所以添加向item添加view这一操作,证实了itme复用的实现。
当前一个项目会有很多的Adapter与它所对应的Model,所以这样会产生很多的Adapter类与Model,这样代码量就会很大,并且还代码重复多,这是一个烦人的过程,所以需要一个万能的模板Adapter,一个Adapter,所有的ListView都能用
对此我直接上源码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
78package com.example.againadapter;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
public class CommonViewHolder {
private SparseArray< View> mViews;
private View Fview;
/*
* 构造函数,设置Tag,
*/
private CommonViewHolder(Context context, ViewGroup parent, int layoutId,
int position){
this.mViews=new SparseArray<View>();
Fview=LayoutInflater.from(context).inflate(layoutId, parent,false);
Fview.setTag(this);
}
/*
* 得到Tag
*/
public static CommonViewHolder get(Context context, View convertView,
ViewGroup parent, int layoutId, int position){
if(convertView==null){
return new CommonViewHolder(context,parent,layoutId,position);
}
return (CommonViewHolder)convertView.getTag();
}
/*
* 最重要获取布局文件中的元素
*/
"unchecked") (
public <T extends View> T getView(int viewId) {
View view = (View) mViews.get(viewId); //从 集合中找,没有找到从当前所在布局文件中找
if (view == null)
{
view = Fview.findViewById(viewId);
mViews.put(viewId, view);
}
return (T) view;
}
public View getView(){
return Fview;
}
/*
* 以下全是设置元素信息的函数
*/
public void setText(int textId,String textView){
TextView tv=getView(textId);
tv.setText(textView);
}
public void setImageResource(int imageViewId,int imageView){
ImageView im=getView(imageViewId);
im.setImageResource(imageView);
}
public void setImageDrawable(int imageViewId,Drawable drawable){
ImageView im=getView(imageViewId);
im.setImageDrawable(drawable);
}
public void setImageBitmap(int imageViewId,Bitmap bitmap){
ImageView im=getView(imageViewId);
im.setImageBitmap(bitmap);
}
}
以上代码是不是简短,但是很精辟
1 | package com.example.againadapter; |
1 | package com.example.againadapter; |
以上是看完张鸿洋博客,依葫芦画瓢写的一个例子,博文地址http://blog.csdn.net/lmj623565791/article/details/38902805/
]]>CMS (Concurrent Mark Sweep)收集器(并发低停顿收集器)
G1收集器
我的IPV4 地址首选地址为 192.168.254.1 ,所以我将虚拟机中NET连接的网关设置为 192.168.254.1。
如图:
当然不要忘记把子网IP(network)改成 192.168.254.0;保证在同一网段。
如图:
之后设置Ubuntu server 中的IP,网关,network,DNS。
我们看到我的可用IP地址为 192.168.199.254.128~192.168.199.254.254,所以我们设置的IP地址必须为此范围内。
设置hosts文件中的IP,sudo vim /etc/hosts
此地址与1步骤中的IP一致。
设置DNS服务器,sudo vim /etc/resolv.conf
完成以上,重启服务就OK了。
在这里着重提醒一下,网关和dns服务器尽量填写首选ip地址(192.168.254.1)不然在后面会出现host unreachable这个问题
]]>1 | 1.java.java------->GBK(操作系统)------>UNICODE(.class JDK)最后为UNICODE的编码.class |
1 | 结构图: |
1 | drop table t1;(t1是表的名字)删除表空间 |
在创建密钥时,一定要进入cd /
中,然后一直回车就行了,有时会出各种幺蛾子
remoteName
就是git remote add [name] [url]中的name
origin git@github.com:Mrnoknow/mrnoknow.github.io.git (fetch)
origin git@github.com:Mrnoknow/mrnoknow.github.io.git (push)
origin 就是对应的remoteName,也就是remote add [name] 中的name,
localBranchName 就是远程仓库的分支名字
Iterable(不是Iterator,这里需要说:Iterable接口是Collection接口的父接口)
新增了一个forEach(Consumer action)默认方法,Collection直接调用改方法而该方法需要函数式
接口(是不是函数式编程,一脸懵逼~可能不是)。
需要说的是:Consumer的accept(T t)方法(是该接口的唯一方法),因为Consumer事函数式接口。
1 | noknow.forEach(obj->System.out.println("Your name:"+obj)); |
Iterator新增forEachRemaining(Consumer action)方法
此方法与Iterable接口不同的是集合必须获取Iterator迭代器
1 | Iterator mr=noknow.iterator(); |
课外扩展:Iterator为快速失败(fail-fast)机制,就是如果有多线程对同一个集合进行操作,如果有
一个线程进行了修改操作,程序就会发生异常,而不会使用修改的值,以此避免共享资源潜在的危险。
在这个简易的程序中,所需要的是jsoup包中的1
2
3
4
5
6import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
这几个类。
主要是解析网页中的标签,直接抽取网页链接
1 | import java.io.IOException; |
以上是工具类,主要用于抓取网页链接。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
31import java.util.ArrayList;
import java.util.LinkedList;
public class UrlOperate {
private LinkedList<String> linked=new LinkedList<String>();//待爬取链接
private ArrayList<String> arry=new ArrayList<String>();//以爬取链接
public void LinkedAdd(String url){//添加待爬取链接
linked.add(url);
}
public void Remove(String url){//爬取链接移除
linked.remove(url);
}
public String GetUrl(){//得到待爬取第一个链接
return linked.getFirst();
}
public boolean IsEmpty(){//判断待爬取链接是否为空
return linked.isEmpty();
}
public LinkedList<String> getHash(){
return linked;
}
public void ArrayAdd(String url){//以爬取链接的添加
arry.add(url);
}
public boolean CompareUrl(String url){//判断将爬取链接是否已存在于以爬取链接中
return arry.contains(url);
}
}
以上代码为网页链接的存取以及判断是否被爬取,对象类。1
2
3
4
5
6
7
8
9import java.io.IOException;
public class Spider {
public static void main(String args[]) throws IOException{
String line=null;
String url="http://mrnoknow.github.io/";
DocOperate operate=new DocOperate();
operate.LinkAcquisition(url);
}
}
完成。
]]>1 | 符号扩展(有符号数) |
和1
零扩展(无符号数)
1 | short x=-1 //二进制补码:10000001; |
上面就是二进制符号扩展。
1 | short x=-1 //二进制补码:10000001; |
零扩展。
]]>