安卓电量优化之JobScheduler使用介绍
版权声明:本文出自汪磊的博客,转载请务必注明出处。
一、JobScheduler概述
JobScheduler是安卓5.0版本推出的API,允许开发者在符合某些条件时创建执行在后台的任务。在Android开发中,会存在这些场景:你需要在稍后的某个时间点或者当满足某个特定的条件时执行一个任务,例如当设备接通电源适配器或者连接到WIFI,此时就可以使用JobScheduler了,当一系列预置的条件被满足时,JobScheduler API为你的应用执行一个操作。与AlarmManager不同的是这个执行时间是不确定的。除此之外,JobScheduler API允许同时执行多个任务。
JobSchedule的宗旨就是把一些不是特别紧急的任务放到更合适的时机批量处理。这样做有两个好处:避免频繁的唤醒硬件模块,造成不必要的电量消耗以及避免在不合适的时间(例如低电量情况下、弱网络或者移动网络情况下的)执行过多的任务消耗电量。
JobSchedule适用版本为5.0及以上,目前还未发现兼容库支持。
二、JobScheduler使用
使用JobScheduler首先我们需要创建一个类继承自JobService且必须实现两个方法(JobService继承自Service),分别是onStartJob(JobParameters params)
和 onStopJob(JobParameters params),如下:
public class TestJobService extends JobService { @Override
public boolean onStartJob(JobParameters params) { return false;
} @Override
public boolean onStopJob(JobParameters params) { return false;
}
}
当任务开始时会执行onStartJob(JobParameters params)
方法,这是系统用来触发已经被执行的任务。这个方法返回一个boolean值。
如果返回值是false,系统假设这个方法返回时任务已经执行完毕。
如果返回值是true,那么系统假定这个任务正要被执行,执行任务的重担就落在了开发者的肩上,当任务执行完毕时开发者需要自己调用jobFinished(JobParameters params, boolean needsRescheduled)
来通知系统。
当系统接收到一个取消请求时,会调用onStopJob(JobParameters params)
方法取消正在等待执行的任务。很重要的一点是如果onStartJob(JobParameters params)
返回false,那么系统假定在接收到一个取消请求时已经没有正在运行的任务。onStopJob(JobParameters params)
在这种情况下不会被调用。
需要注意的是这个job service运行在你的主线程,这意味着你需要使用子线程,handler, 或者一个异步任务来运行耗时的操作以防止阻塞主线程。我们可以在onStartJob
中利用Handler发送消息,在Handler中处理相关任务。
jobFinished(JobParameters params, boolean needsRescheduled)
的两个参数中的params参数是从JobService的onStartJob(JobParameters params)
的params传递过来的,needsRescheduled参数是告诉系统这个任务如果由于某些原因导致执行失败是否需要重新调度执行,true需要重新调度执行,false不需要。
最后我们需要到AndroidManifest.xml中添加一个service节点让你的应用拥有绑定和使用这个JobService的权限。
<service
android:name=".TestJobService"
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
在创建完TestJobService后,我们就该创建JobScheduler对象了,创建JobScheduler对象需要通过getSystemService( Context.JOB_SCHEDULER_SERVICE )来
初始化:
JobScheduler tm =(JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
接下来我们使用JobInfo.Builder
来构建一个JobInfo对象。JobInfo.Builder接收两个参数,第一个参数是你要运行的任务的标识符,简单理解为这个任务的ID就可以了,第二个是这个Service组件的类名。
JobInfo.Builder builder = new JobInfo.Builder(kJobId++, mServiceComponent);
mServiceComponent初始化如下:
mServiceComponent = new ComponentName(this, TestJobService.class);
builder允许你设置很多不同的选项来控制任务的执行,重要方法说明如下:
方法名 | 说明 |
setPeriodic(long intervalMillis) | 设置任务每隔intervalMillis运行一次 |
setMinimumLatency(long minLatencyMillis) | 这个函数能让你设置任务的延迟执行时间(单位是毫秒),这个函数与setPeriodic(long time) 方法不兼容,如果这两个方法同时调用了就会引起异常 |
setOverrideDeadline(long maxExecutionDelayMillis) | 这个方法让你可以设置任务最晚的延迟时间。如果到了规定的时间时其他条件还未满足,你的任务也会被启动。与setMinimumLatency(long time) 一样,这个方法也会与setPeriodic(long time) ,同时调用这两个方法会引发异常 |
setPersisted(boolean isPersisted) | 这个方法告诉系统当你的设备重启之后你的任务是否还要继续执行 |
setRequiredNetworkType(int networkType) | 这个方法让你这个任务只有在满足指定的网络条件时才会被执行。默认条件是JobInfo.NETWORK_TYPE_NONE,这意味着不管是否有网络这个任务都会被执行。另外两个可选类型,一种是JobInfo.NETWORK_TYPE_ANY,它表明需要任意一种网络才使得任务可以执行。另一种是JobInfo.NETWORK_TYPE_UNMETERED,它表示设备不是蜂窝网络( 比如在WIFI连接时 )时任务才会被执行 |
setRequiresCharging(boolean requiresCharging) | 这个方法告诉你的应用,只有当设备在充电时这个任务才会被执行 |
setRequiresDeviceIdle(boolean requiresDeviceIdle) | 这个方法告诉你的任务只有当用户没有在使用该设备且有一段时间没有使用时才会启动该任务 |
需要注意的是setRequiredNetworkType(int networkType)
, setRequiresCharging(boolean requireCharging)
and setRequiresDeviceIdle(boolean requireIdle)
者几个方法可能会使得你的任务无法执行,除非调用setOverrideDeadline(long time)
设置了最大延迟时间,使得你的任务在未满足条件的情况下也会被执行。一旦你预置的条件被设置,你就可以构建一个JobInfo对象,然后通过如下所示的代码将它发送到你的JobScheduler中
if( tm.schedule( builder.build() ) <= 0 ) {
//something wrong
}
schedule方法会返回一个整型。如果schedule方法失败了,它会返回一个小于0的错误码。否则返回我们在JobInfo.Builder中定义的标识id。
停止某个任务,可以调用JobScheduler对象的cancel(int jobId)
来实现;如果想取消所有的任务,可以调用JobScheduler对象的cancelAll()
来实现。
以上就是JobScheduler的介绍,是不是很简单,重要的是理解什么情况下使用JobScheduler。
三、JobScheduler实例举例
本Demo是安卓官方提供的一个例子,我们先看TestJobService类;
public class TestJobService extends JobService {
private static final String TAG = "SyncService"; @Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "Service created");
} @Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "Service destroyed");
} @Override
public int onStartCommand(Intent intent, int flags, int startId) {
Messenger callback = intent.getParcelableExtra("messenger");
Message m = Message.obtain();
m.what = MainActivity.MSG_SERVICE_OBJ;
m.obj = this;
try {
callback.send(m);
} catch (RemoteException e) {
Log.e(TAG, "Error passing service object back to activity.");
}
return START_NOT_STICKY;
} @Override
public boolean onStartJob(JobParameters params) {
// We don't do any real 'work' in this sample app. All we'll
// do is track which jobs have landed on our service, and
// update the UI accordingly.
jobParamsMap.add(params);
if (mActivity != null) {
mActivity.onReceivedStartJob(params);
}
Log.i(TAG, "on start job: " + params.getJobId());
return true;
} @Override
public boolean onStopJob(JobParameters params) {
// Stop tracking these job parameters, as we've 'finished' executing.
jobParamsMap.remove(params);
if (mActivity != null) {
mActivity.onReceivedStopJob();
}
Log.i(TAG, "on stop job: " + params.getJobId());
return true;
} MainActivity mActivity;
private final LinkedList<JobParameters> jobParamsMap = new LinkedList<JobParameters>(); public void setUiCallback(MainActivity activity) {
mActivity = activity;
} /** Send job to the JobScheduler. */
public void scheduleJob(JobInfo t) {
Log.d(TAG, "Scheduling job");
JobScheduler tm =
(JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
if(tm.schedule(t)<=0){
Log.i(TAG, "something wrong");
}
} /**
* Not currently used, but as an exercise you can hook this
* up to a button in the UI to finish a job that has landed
* in onStartJob().
*/
public boolean callJobFinished() {
JobParameters params = jobParamsMap.poll();
if (params == null) {
return false;
} else {
jobFinished(params, false);
return true;
}
}
}
上面提到过JobService其实也是Service,同样具有相应生命周期方法。TestJobService先不做详细讲解,但是要看到这个类中封装了一些JobScheduler方法供外界调用。
接下来我们结合MainActivity类一起看,MainActivity源码如下:
public class MainActivity extends Activity { private static final String TAG = "MainActivity"; public static final int MSG_UNCOLOUR_START = 0;
public static final int MSG_UNCOLOUR_STOP = 1;
public static final int MSG_SERVICE_OBJ = 2;
// UI fields.
int defaultColor;
int startJobColor;
int stopJobColor; private TextView mShowStartView;
private TextView mShowStopView;
private TextView mParamsTextView;
private EditText mDelayEditText;
private EditText mDeadlineEditText;
private RadioButton mWiFiConnectivityRadioButton;
private RadioButton mAnyConnectivityRadioButton;
private CheckBox mRequiresChargingCheckBox;
private CheckBox mRequiresIdleCheckbox;
ComponentName mServiceComponent;
TestJobService mTestService;
//
private static int kJobId = 0; @Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sample_main);
Resources res = getResources();
defaultColor = res.getColor(R.color.none_received);
startJobColor = res.getColor(R.color.start_received);
stopJobColor = res.getColor(R.color.stop_received);
// Set up UI.
mShowStartView = (TextView) findViewById(R.id.onstart_textview);
mShowStopView = (TextView) findViewById(R.id.onstop_textview);
mParamsTextView = (TextView) findViewById(R.id.task_params);
mDelayEditText = (EditText) findViewById(R.id.delay_time);
mDeadlineEditText = (EditText) findViewById(R.id.deadline_time);
mWiFiConnectivityRadioButton = (RadioButton) findViewById(R.id.checkbox_unmetered);
mAnyConnectivityRadioButton = (RadioButton) findViewById(R.id.checkbox_any);
mRequiresChargingCheckBox = (CheckBox) findViewById(R.id.checkbox_charging);
mRequiresIdleCheckbox = (CheckBox) findViewById(R.id.checkbox_idle);
mServiceComponent = new ComponentName(this, TestJobService.class);
// Start service and provide it a way to communicate with us.
Intent startServiceIntent = new Intent(this, TestJobService.class);
startServiceIntent.putExtra("messenger", new Messenger(mHandler));
startService(startServiceIntent);
} Handler mHandler = new Handler(/* default looper */) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_UNCOLOUR_START:
mShowStartView.setBackgroundColor(defaultColor);
break;
case MSG_UNCOLOUR_STOP:
mShowStopView.setBackgroundColor(defaultColor);
break;
case MSG_SERVICE_OBJ:
mTestService = (TestJobService) msg.obj;
mTestService.setUiCallback(MainActivity.this);
}
}
}; private boolean ensureTestService() {
if (mTestService == null) {
Toast.makeText(MainActivity.this, "Service null, never got callback?",
Toast.LENGTH_SHORT).show();
return false;
}
return true;
} /**
* UI onclick listener to schedule a job. What this job is is defined in
* TestJobService#scheduleJob().
*/
@SuppressLint("NewApi")
public void scheduleJob(View v) {
if (!ensureTestService()) {
return;
}
JobInfo.Builder builder = new JobInfo.Builder(kJobId++, mServiceComponent); String delay = mDelayEditText.getText().toString();
if (delay != null && !TextUtils.isEmpty(delay)) {
builder.setMinimumLatency(Long.valueOf(delay) * 1000);
}
String deadline = mDeadlineEditText.getText().toString();
if (deadline != null && !TextUtils.isEmpty(deadline)) {
builder.setOverrideDeadline(Long.valueOf(deadline) * 1000);
}
boolean requiresUnmetered = mWiFiConnectivityRadioButton.isChecked();
boolean requiresAnyConnectivity = mAnyConnectivityRadioButton.isChecked();
if (requiresUnmetered) {
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);
} else if (requiresAnyConnectivity) {
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
}
builder.setRequiresDeviceIdle(mRequiresIdleCheckbox.isChecked());
builder.setRequiresCharging(mRequiresChargingCheckBox.isChecked());
mTestService.scheduleJob(builder.build());
}
/**
* cancel All jobs
* @param v
*/
@SuppressLint("NewApi")
public void cancelAllJobs(View v) {
JobScheduler tm =
(JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
tm.cancelAll();
} /**
* UI onclick listener to call jobFinished() in our service.
*/
public void finishJob(View v) {
if (!ensureTestService()) {
return;
}
mTestService.callJobFinished();
mParamsTextView.setText("");
} /**
* Receives callback from the service when a job has landed
* on the app. Colours the UI and post a message to
* uncolour it after a second.
*/
@SuppressLint("NewApi")
public void onReceivedStartJob(JobParameters params) {
mShowStartView.setBackgroundColor(startJobColor);
Message m = Message.obtain(mHandler, MSG_UNCOLOUR_START);
mHandler.sendMessageDelayed(m, 1000L); // uncolour in 1 second.
mParamsTextView.setText("Executing: " + params.getJobId() + " " + params.getExtras());
} /**
* Receives callback from the service when a job that
* previously landed on the app must stop executing.
* Colours the UI and post a message to uncolour it after a
* second.
*/
public void onReceivedStopJob() {
mShowStopView.setBackgroundColor(stopJobColor);
Message m = Message.obtain(mHandler, MSG_UNCOLOUR_STOP);
mHandler.sendMessageDelayed(m, 2000L); // uncolour in 1 second.
mParamsTextView.setText("");
} }
对于这个Demo我只想说一个点,MainActivity在启动的时候onCreate()方法中47-49行启动了TestJobService,并且传递了一个new Messenger(mHandler)匿名对象,接下来我们看下TestJobService中onStartCommand方法,此方法上来就获取Messenger对象,并且调用send方法发送一个消息(实际调用的就是Handler的send方法,可自行点进源码查看),此消息obj为TestJobService类实例对象。回到MainActivity中查看mHandler,what为MSG_SERVICE_OBJ的时候:将msg的obj赋值给MainActivity中mTestService变量,然后调用TestJobService中setUiCallback方法,这样MainActivity与TestJobService类就互相持有彼此实例对象了,也就可以调用其内部的方法互相通信了,比如TestJobService中onStartJob方法调用MainActivity中的onReceivedStartJob方法,onReceivedStartJob方法中其实就是发送了Handler消息,最后在Handler中执行相关业务逻辑。好了Demo就说这一点,其余请自行细细查看,很简单,最后附上Demo布局文件:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" > <LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="vertical"> <LinearLayout
android:layout_width="match_parent"
android:layout_height="100dp">
<TextView
android:id="@+id/onstart_textview"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#999999"
android:gravity="center"
android:text="@string/onstarttask"/>
<TextView
android:id="@+id/onstop_textview"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@color/none_received"
android:gravity="center"
android:text="@string/onstoptask"/>
</LinearLayout>
<Button
android:id="@+id/finished_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="20dp"
android:layout_marginBottom="5dp"
android:onClick="finishJob"
android:text="@string/finish_job_button_text"/> <TextView
android:id="@+id/task_params"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/defaultparamtext"
android:gravity="center"
android:textSize="20dp"
android:padding="15dp"
android:layout_marginBottom="10dp" /> <TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/constraints"
android:layout_margin="15dp"
android:textSize="18dp"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginLeft="10dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/connectivity"
android:layout_marginRight="10dp"/>
<RadioGroup
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<RadioButton android:id="@+id/checkbox_any"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/any"
android:checked="false" />
<RadioButton android:id="@+id/checkbox_unmetered"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/unmetered"
android:checked="false" />
</RadioGroup> </LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/timing"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:textSize="17dp"
android:text="@string/delay"/>
<EditText
android:id="@+id/delay_time"
android:layout_width="60dp"
android:layout_height="wrap_content"
android:inputType="number"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/deadline"
android:textSize="17dp"/>
<EditText
android:id="@+id/deadline_time"
android:layout_width="60dp"
android:layout_height="wrap_content"
android:inputType="number"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/charging_caption"
android:layout_marginRight="15dp"/>
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/checkbox_charging"
android:text="@string/charging_text"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/idle_caption"
android:layout_marginRight="15dp"/>
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/checkbox_idle"
android:text="@string/idle_mode_text"/>
</LinearLayout> </LinearLayout>
<Button
android:id="@+id/schedule_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginLeft="40dp"
android:layout_marginRight="40dp"
android:onClick="scheduleJob"
android:text="@string/schedule_job_button_text"/>
<Button
android:id="@+id/cancel_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="40dp"
android:layout_marginRight="40dp"
android:onClick="cancelAllJobs"
android:text="@string/cancel_all_jobs_button_text"/>
</LinearLayout>
</ScrollView>
好了,本篇到此结束,希望通过本篇介绍你能正确使用JobScheduler这个强大的工具,有些任务放在适合的时机来做更加适宜。
安卓电量优化之JobScheduler使用介绍的更多相关文章
- 安卓电量优化之AlarmManager使用全部解析
版权声明:本文出自汪磊的博客,转载请务必注明出处. 一.AlarmManager概述 AlarmManager是安卓系统中一种系统级别的提示服务,可以在我们设定时间或者周期性的执行一个intent,这 ...
- 安卓电量优化之WakeLock锁机制全面解析
版权声明:本文出自汪磊的博客,转载请务必注明出处. 一.WakeLock概述 wakelock是一种锁的机制,只要有应用拿着这个锁,CPU就无法进入休眠状态,一直处于工作状态.比如,手机屏幕在屏幕关闭 ...
- Android性能优化系列之电量优化
电量消耗的计算与统计是一件麻烦而且矛盾的事情,记录电量消耗本身也是一个费电量的事情,随着Android开的性能要求越来越高,电量的优化,也显得格外重要,一个耗电的应用,用户肯定会毫不犹豫的进行卸载,所 ...
- Android性能优化-电量优化
前言 电量优化,这个名词在传统PC时代,我们基本很少听见.然而到了诺基亚时代,我们也同样很少关注.直到了移动互联的智能机时代.电量优化才被慢慢的重视起来.可能的原因如下: 移动设备,不能一直使用电源供 ...
- 安卓性能优化之清除Handler的Message和Runnable
安卓性能优化之清除Handler的Message和Runnable Handler是由系统所提供的一种异步消息处理的常用方式,一般情况下不会发生内存泄露. 但既然是调优,当在A_Activity中使用 ...
- ReSharper的功能真的很强大主要是针对代码规范和优化,园子里介绍的也不少,如果你没有安装,那我只能表示你们会相见恨晚
二.ReSHarper 代码规范.单元测试.... ReSharper的功能真的很强大,主要是针对代码规范和优化,园子里介绍的也不少,如果你没有安装,那我只能表示你们会相见恨晚! 1.像命名不规范,f ...
- 安卓手机优化 ROOT自启动管理 + 电量管理
一.KingRoot + 净化大师 KingRoot 主要完成ROOT 和 自启动软件的管理 这里禁止的自启动管理 对于有些软件是不太管用 比如美团 手机百度等 净化大师 主要完成 高电量的软件优化 ...
- mysql性能优化学习笔记-参数介绍及优化建议
MySQL服务器参数介绍 mysql参数介绍(客户端中执行),尽量只修改session级别的参数. 全局参数(新连接的session才会生效,原有已经连接的session不生效) set global ...
- loadrunner脚本优化-ParameterList参数类型介绍
脚本优化-Parameter List参数类型介绍 by:授客 QQ:1033553122 篇幅问题,这里采用网盘下载的方式和大家分享: 百度网盘分享: 链接: http://pan.baidu.co ...
随机推荐
- 阿里云部署Docker(6)----解决删除<none>镜像问题
转载请注明来源,本博客原创作者为:http://blog.csdn.net/minimicall?viewmode=contents 在Docker使用中,常常会碰到删除镜像不成功.反而让镜像变成了& ...
- org.hibernate.LazyInitializationException could not initialize proxy-no Session的解决
方法一:在web.xml配置延迟关闭session的过滤器 <!-- 延迟关闭session OpenSessionInViewFilter 的顺序位于struts2过滤之上 否则延迟关闭ses ...
- centos yum安装ffmpeg
ffmpeg是一个重要的应用软件,用于执行与视频文件转换成不同的视频流格式的视频站点,能够安装在linux系统上来使用 (一)安装编译环境 #yum install -y automake auto ...
- DirectX:在graph自己主动连线中增加自己定义filter(graph中遍历filter)
为客户提供的视频播放的filter的測试程序中,採用正向手动连接的方式(http://blog.csdn.net/mao0514/article/details/40535791).因为不同的视频压缩 ...
- Eclipse的Servers视图中无法加入Tomcat6/Tomcat7
引言: 在基于Eclipse的开发过程中,出现了无法在Eclipse中加入Tomcat的问题,经过从网上搜索之后.找到了答案. 问题的提出: 无法从下面方式,加入Tomcatserver. 当中Se ...
- 腾讯Web工程师的前端书单
2014年一月以来,自己接触web前端开发已经两年多了,记录一下自己前端学习路上看过的,以及道听途说的一些书,基本上按照由浅入深来介绍. JavaScript 入门 <JavaScript权威指 ...
- sql语句百万数据量优化方案
一:理解sql执行顺序 在sql中,第一个被执行的是from语句,每一个步骤都会产生一个虚拟表,该表供下一个步骤查询时调用,比如语句:select top 10 column1,colum2,max( ...
- Eclipse 插件安装、升级和卸载的方法
Eclipse 的插件可以装在内部,也可以装在外部,装在内部的方法很简单:把插件的features和plugins目录copy到eclipse的安装目录即可. eclipse和其插件升级比较频繁,用过 ...
- CentOS openssh升级至7.4版本
1.原环境: cat /etc/issue CentOS release 6.5 (Final) ssh -V OpenSSH_7.3p1, OpenSSL 1.0.1e-fips 11 Feb 20 ...
- C语言课程设计(成绩管理系统)
C语言课程设计(成绩管理系统) 翻到了大学写的C语言课程设计,缅怀一下 内容: 增加学生成绩 查询学生成绩 删除 按照学生成绩进行排序 等 #include <stdio.h> #incl ...