JAVA-泛型理解

一直对java泛型一知半解,决定好好理解一下泛型.


##泛型的基本使用:
我们最常用到泛型的地方可能是arraylist中,如:ArrayList<Integer> intList = new ArrayList<Integer>();它可以构造各种类型的变量来组装成不同的list而不必对每一个类型都创建一个Arraylist的类,

  • 定义及使用:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    //定义
    class Person<T>{// 此处可以随便写标识符号   
        private T x ;        
        private T y ;        
        public void setAge(T x){//作为参数  
            this.x = x ;  
        }  
        public void setName(T y){  
            this.y = y ;  
        }  
        public T getAge(){//作为返回值  
            return this.x ;  
        }  
        public T getName(){  
            return this.y ;  
        }  
    };

因为T表示派生自Object类的任何类,所以尖括号T中必须是继承自Object,如:String,Interger,不能使用原始的类型,如int,double.

1
2
3
4
5
//使用
//设置年龄 
Person<Integer> p = new Person<Integer>() ;   
p.setAge(new Integer(100)) ;   
System.out.println(p.getAge());

1
2
3
4
//设置名字  
Person<String> p = new Person<String>() ;   
p.setName(new String("name")) ;   
System.out.println(p.getName());

###使用的Class object来传递类的class对象
我们在解析Json的时候:

1
2
3
4
public static List<SuccessModel> parseSuccessArray(String response){
List<SuccessModel> modelList = JSON.parseArray(response, SuccessModel.class);
return modelList;
}

这样做比较的死板,我们可以吧successmodel抽取出来,当做一个变量,这样就可以使用到泛型了,如下:

1
2
3
4
public static <T> List<T> parseArray(String response,Class<T> object){
List<T> modelList = JSON.parseArray(response, object);
return modelList;
}

注意到,我们用的Class object来传递类的class对象,即我们上面提到的SuccessModel.class,这样就可以传递任意的class来解析不同的数据.

//待续。。。。

##进阶

###类型绑定:
有时候定义泛型的时候可能希望泛型类型只能是一部分类型,就是给泛型指定一个界限,
定义形式为:

1
<T extends BoundingType>

T和BoundingType可以是类,也可以是接口。另外注意的是,此处的”extends“表示的子类型,不等同于继承。在这里extends后的BoundingType可以是类,也可以是接口.

###通配符:
通配符用?表示无边界通配符,通配符的意义就是他是一个未知的符号,可以表示任意的类,如:

1
2
3
4
5
Point<?> point;
point = new Point<Integer>(3,3);
point = new Point<Float>(4.3f,4.3f);
point = new Point<Double>(4.3d,4.90d);
point = new Point<Long>(12l,23l);

  • 通配符?和T的区别:
    泛型变量T不能在代码用于创建变量,只能在类,接口,函数中声明以后,才能使用。
    而无边界通配符?则只能用于填充泛型变量T,表示通配任何类型
    如:
    1
    2
    Box<?> box;
    box = new Box<String>();

?只能出现在Box<?> box;中,其它位置都是不对的

  • 通配符?的extends绑定:
    通配符可以和泛型一样加以限定。如:
    1
    Point<? extends Number> point;

注意:利用<? extends Number>定义的变量,只可取其中的值,不可修改
如:

1
Point<? extends Number> point = new Point<Integer>(3);

point的类型是由Point<? extends Number>决定的,并不会因为point = new Point(3);而改变类型。如果会改变类型那么<? extends Number>就失去了作用。但取值时,正由于泛型变量T被填充为<? extends Number>,所以编译器能确定的是T肯定是Number的子类,编译器就会用Number来填充T。

###通配符?的super绑定:
如果说 <? extends XXX>指填充为派生于XXX的任意子类的话,那么<? super XXX>则表示填充为任意XXX的父类.
如:

1
2
3
4
5
6
7
8
9
10
11
12
13
class CEO extends Manager {
}
class Manager extends Employee {
}
class Employee {
}
List<? super Manager> list;
list = new ArrayList<Employee >();
list = new ArrayList<Manager >();
list = new ArrayList<CEO >();//编译会报错

因为CEO类已经不再是Manager的父类了。所以会报编译错误。
注意:super通配符表示的实例内容:能存不能取
先看存的部分:

1
2
3
4
5
6
List<? super Manager> list;
list = new ArrayList<Employee>();
//存
list.add(new Employee()); //编译错误
list.add(new Manager());
list.add(new CEO());

为什么会报编译错误呢?因为
编译器无法确定<? super Manager>的具体类型,但唯一可以确定的是Manager()、CEO()肯定是<? super Manager>的子类,所以肯定是可以add进去的。但Employee不一定是<? super Manager>的子类,所以不能确定,不能确定的,肯定是不允许的,所以会报编译错误。
super和extends相反,使用super关键字时,往里存的只要是Employee的子类,就会强制转换为Employee存储,但是取出来时应该都为父类Employee所以没有意义,所以我们认为super通配符:能存不能取;

##总结
extends 和 the ? super 通配符的特征,我们可以得出以下结论:
◆ 如果你想从一个数据类型里获取数据,使用 ? extends 通配符(能取不能存)
◆ 如果你想把对象写入一个数据结构里,使用 ? super 通配符(能存不能取)
◆ 如果你既想存,又想取,那就别用通配符。

参考链接:
夯实JAVA基本之一 —— 泛型详解(1):基本使用
夯实JAVA基本之一——泛型详解(2):高级进阶

使用mvp+rxjava+retrofit加载数据

将mvp和rxjava和retrofit简单整合的列子,让activity的代码不再那么臃肿,只负责显示数据.


关于mvp的介绍可以看这里
关于Rxjava的介绍可以看这里;
关于retrofit的介绍可以看这里(看官方文档应该是最有效率的2333…);
首先看一下获取数据的接口链接:
http://apicloud.mob.com/v1/weather/query?province=%E6%B9%96%E5%8D%97&key=520520test&city=%E9%95%BF%E6%B2%99;
普通的请求方式是这样的:
定义一个接口:

1
2
3
4
5
6
public interface ApiStores {
String API_SERVER_URL = "http://apicloud.mob.com/v1/weather/";
String KEY = "....";
@GET("query")
Call<WeatherModel> getWeatherByAddress(@Query("key") String key,@Query("province") String province, @Query("city") String city);
}

创建一个Retrofit对象:

1
2
3
4
5
6
7
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(ApiStores.API_SERVER_URL)
//增加返回值为Gson的支持(以实体类返回)
.addConverterFactory(GsonConverterFactory.create())
.build();
ApiStores service = retrofit.create(ApiStores.class);
Call<WeatherModel> repos=service.getWeatherByAddress("湖南","长沙");

然后通过返回的call 来发出网络请求:

1
2
3
4
5
6
7
8
9
10
11
call.enqueue(new Callback<String>() {
@Override
public void onResponse(Call<String> call, Response<WeatherModel> response)
{
Log.e("===","return:"WeatherModel.getname);//返回值直接就是对象,简直不要太爽...
}
@Override
public void onFailure(Call<String> call, Throwable t) {
Log.e("===","失败");
}
});

这样已经可以完成数据的请求了,但是这样代码都在activity里,如果代码变多activity会变得臃肿起来,不够优雅…╮(╯_╰)╭


使用mvp+rxjava+retrofit:
retrofit对Rxjava的支持是非常好的,可以直接返回可观察者,像这样:

1
2
@GET("query")
Observable<WeatherModel> getWeatherByAddress(@Query("key") String key,@Query("province") String province, @Query("city") String city);

我们拿到可观察者后,就可以订阅事件了:

1
2
3
mCompositeSubscription.add(observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(subscriber));

rxjava最nb的地方就是它可以很方便的切换线程不需要使用各种runOnUiThread
完整代码可以看这个;

安卓全局的复制粘贴--Clipboard

#安卓全局Clipboard

Clipboard官方文档在此(需要科学上网…)
Clioboard 的官方解释是这样的:


Android provides a powerful clipboard-based framework for copying and pasting. It supports both simple and complex data types, including text strings, complex data structures, text and binary stream data, and even application assets


大概意思就是:
Android提供了一个强大的复制和粘贴clipboard-based框架。它同时支持的简单和复杂数据类型,包括文本字符串,复杂的数据结构,文本和二进制数据流,甚至应用程序资源.
Clipboard支持三种形式的数据传递:text,URL,Intent.

某宝的口令应该就是之类的方式来实现的。

##HOW TO USE:

直接上官方的示例代码

###获取Clipboard服务:

1
2
3
// Gets a handle to the clipboard service.
ClipboardManager clipboardmanager = (ClipboardManager)       
getSystemService(Context.CLIPBOARD_SERVICE);

###复制:

复制text:

1
2
3
// Creates a new text clip to put on the
clipboardClipData clip = ClipData.newPlainText("simple text","Hello, World!");
clipboardmanager.setPrimaryClip(clip);

复制URL:

1
2
3
4
5
6
7
8
9
10
11
12
// Creates a Uri based on a base Uri and a record ID based on the contact's last name
// Declares the base URI string
private static final String CONTACTS = "content://com.example.contacts";
// Declares a path string for URIs that you use to copy data
private static final String COPY_PATH = "/copy";// Declares the Uri to paste to the clipboard
Uri copyUri = Uri.parse(CONTACTS + COPY_PATH + "/" + lastName);
...
// Creates a new URI clip object. The system uses the anonymous getContentResolver() object to
// get MIME types from provider. The clip object's label is "URI", and its data is
// the Uri previously created.
ClipData clip = ClipData.newUri(getContentResolver(),"URI",copyUri);
clipboardmanager.setPrimaryClip(clip);

复制Intent:

1
2
3
4
5
6
// Creates the IntentIntent appIntent = new Intent(this, com.example.demo.myapplication.class);
...
// Creates a clip object with the Intent in it. Its label is "Intent" and its data is
// the Intent object created previously
ClipData clip = ClipData.newIntent("Intent",appIntent);
clipboardmanager.setPrimaryClip(clip);

###粘贴:

获取的方式都差不多
获取:

1
2
3
4
5
6
7
8
ClipData clipData = mClipboardManager.getPrimaryClip();
ClipData.Item item = clipData.getItemAt(0);
//获取text
String text = item.getText().toString();
//获取uri
Uri pasteUri = item.getUri();
//获取intent:
Intent intent = item.getIntent();

google还为我们提供了剪切板发生改变的PrimaryClipChangedListener这样当我们获取到剪切板的数据后就可以做各种事情比如获取到用户复制了英文单词则可以帮助翻译,复制了链接则帮助打开等等。
监听剪切板变化的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
mClipboardManager.addPrimaryClipChangedListener(new ClipboardManager.OnPrimaryClipChangedListener() {
@Override
public void onPrimaryClipChanged() {
if (clipboardManager.hasPrimaryClip()){
Toast.makeText(MainActivity.this, "剪切板发生改变" ,Toast.LENGTH_SHORT).show();
ClipData clipData = clipboardManager.getPrimaryClip();
ClipData.Item item = clipData.getItemAt(0);
String text = item.getText().toString();
Toast.makeText(MainActivity.this, "获取到的剪切板文字为:"+text, Toast.LENGTH_SHORT).show();}
else {
Toast.makeText(MainActivity.this, "剪切板上没有数据", Toast.LENGTH_SHORT).show();
}
}
});

Viewpager无限循环(首页与尾页平滑过渡)

#Viewpager无限循环(首页与尾页平滑过渡)

##double kill
在网上找了不少的viewpager无限轮播的例子,大部分都是Interger.MAX_VALUES
滑到最后一页的时候setCurrentItem(0),这样虽然实现了无限轮播,但是当从最后一页跳转到第一页的时候不够优雅。。通过google终于找到了答案:

将你的图片的list的前后各增加一张图片,并将最后一张图片放至图片的第一张,第一张图片
放至图片的最后一张,例如: List {ABCD} 改为{DABCDA}这样滑到最后一张图片的时候会显示一张图片而不是突兀的跳过中间的几张图片。然后初始化viewpager的时候让他从第一张图片开始轮播:setCurrentItem(1)

核心代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Override
public void onPageScrolled(int i, float v, int i1) {
if (v == 0.0){
if (i == 0){
//当滑到第一张图时显示最后一张图并将postion跳至"D"位置
vp.setCurrentItem(mImageViews.length-2,false);
}
//当滑到最后一张图时显示第一张图并将position跳至"A"位置
else if (i == mImageViews.length-1)
{
vp.setCurrentItem(1,false);
}
}
}

##至此就实现了无限轮播。
附上轮播代码:

1
2
3
4
5
6
7
8
9
10
11
12
Timer timer = new Timer();
timer.schedule(new TimerTask()
{
@Override
public void run() {
currentItem++;
//当currentItem等于图片大小的时候记得将 currentItem重置为1
if (currentItem== mImageViews.length) {
currentItem= 1;
}
mHandler.sendEmptyMessage(REFRESH_VP_CODE);
}}, 3000, 3000);

Android静默安装

静默安装

First blood
今天公司有个静默安装的需求,由于是自己的系统,所以操作起来比较容易,无需kengdie的root权限,在网上找了下资料,记录如下:

具体实现

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
static DataOutputStream dataOutputStream = null;
static BufferedReader errorStream = null;
static Process process;
static boolean result = false;
public static boolean slientInstall(String apkPath)
{
try {
process = Runtime.getRuntime().exec("su"); //获取su权限
dataOutputStream = new DataOutputStream(process.getOutputStream());//获取
String command = "pm install -r "+apkPath+"\n"; //安装命令
String path = apkPath;
Log.e("Tag",command);
Log.e("Tag",path+"+++>path");
dataOutputStream.write(command.getBytes(Charset.forName("utf-8")));
dataOutputStream.flush();
dataOutputStream.writeBytes("exit\n");
dataOutputStream.flush();
try {
process.waitFor();
errorStream = new BufferedReader(new InputStreamReader(process.getErrorStream())); //记录安装的信息
String msg = "";
String line;
while ((line = errorStream.readLine())!= null)
{
msg+=line;
}
Log.e("Tag","安装的信息是"+msg);
if (!msg.contains("Failure"))
{
return true;
}
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
catch (IOException e)
{
e.printStackTrace();
}finally {
try {
if (dataOutputStream!=null){
dataOutputStream.close();
}
if (errorStream!= null){
errorStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}

上面就实现了静默安装的功能,需要应用有root 权限才行,第一次写博客代码有点乱,,zzzzz
待续。。

###作者
fuyifang

|