I'm writing an application that renders a sequence of pictures received in real-time from a remote TCP connection into an ImageView element.
The stream is composed of single frames encoded in PGM format and sent at 9Hz I tought that a very low frame rate like this should be handled easily using a background Service that sends fully decoded bitmap to my MainActivity.
Here's my VideoService (I'm posting just run() method since I think it's the only one of some interest):
public void run() {
InetAddress serverAddr = null;
try {
serverAddr = InetAddress.getByName(VIDEO_SERVER_ADDR);
} catch (UnknownHostException e) {
Log.e(getClass().getName(), e.getMessage());
e.printStackTrace();
return;
}
Socket socket = null;
BufferedReader reader = null;
do {
try {
socket = new Socket(serverAddr, VIDEO_SERVER_PORT);
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
boolean frameStart = false;
LinkedList<String> frameList = new LinkedList<>();
while (keepRunning) {
final String message = reader.readLine();
if (!frameStart && message.startsWith("F"))
frameStart = true;
else if (frameStart && message.startsWith("EF")) {
frameStart = false;
final Bitmap bitmap = Bitmap.createBitmap(IR_FRAME_WIDTH, IR_FRAME_HEIGHT, Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(bitmap);
final String[] data = frameList.toArray(new String[frameList.size()]);
canvas.drawBitmap(bitmap, 0, 0, null);
//Log.d(this.getClass().getName(), "IR FRAME COLLECTED");
if ((data.length - 6) == IR_FRAME_HEIGHT) {
float grayScaleRatio = Float.parseFloat(data[2].trim()) / 255.0f;
for (int y = 0; y < IR_FRAME_HEIGHT; y++) {
final String line = data[y + 3];
final String[] points = line.split("\\s+");
if (points.length == IR_FRAME_WIDTH) {
for (int x = 0; x < IR_FRAME_WIDTH; x++) {
final float grayLevel = Float.parseFloat(points[x]) / grayScaleRatio;
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
final int level = (int)grayLevel;
paint.setColor(Color.rgb(level, level, level));
canvas.drawPoint(x, y, paint);
}
} else
Log.d(this.getClass().getName(), "Malformed line");
}
final Intent messageIntent = new Intent();
messageIntent.setAction(VIDEO_BROADCAST_KEY);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
bitmap.recycle();
messageIntent.putExtra(VIDEO_MESSAGE_KEY, stream.toByteArray());
stream.close();
sendBroadcast(messageIntent);
} else
Log.d(this.getClass().getName(), "Malformed data");
frameList.clear();
} else if (frameStart)
frameList.add(message);
}
Thread.sleep(VIDEO_SERVER_RESPAWN);
} catch (Throwable e) {
Log.e(getClass().getName(), e.getMessage());
e.printStackTrace();
}
} while (keepRunning);
if (socket != null) {
try {
socket.close();
} catch (Throwable e) {
Log.e(getClass().getName(), e.getMessage());
e.printStackTrace();
}
}
}
The message is a line coming from the following text:
F
P2
160 120
1226
193 141 158 152 193 186 171 177 186 160 195 182 ... (160 times)
.
. (120 lines)
.
278 248 253 261 257 284 310 304 304 272 227 208 ... (160 times)
EF
In MainActivity I handle this trough this code:
class VideoReceiver extends BroadcastReceiver {
final public Queue<Bitmap> imagesQueue = new LinkedList<>();
#Override
public void onReceive(Context context, Intent intent) {
try {
//Log.d(getClass().getName(), "onReceive() called");
final byte[] data = intent.getByteArrayExtra(VideoService.VIDEO_MESSAGE_KEY);
final Bitmap bitmap = BitmapFactory.decodeByteArray(data,0,data.length);
imagesQueue.add(bitmap);
runOnUiThread(updateVideoTask);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
updateVideoTask task is defined like this:
updateVideoTask = new Runnable() {
public void run() {
if (videoReceiver == null) return;
if (!videoReceiver.imagesQueue.isEmpty())
{
final Bitmap image = videoReceiver.imagesQueue.poll();
if (image == null) return;
videoView.setImageBitmap(image);
Log.d(this.getClass().getName(), "Images to spool: " + videoReceiver.imagesQueue.size());
}
}
};
Unluckly when I run the application I notice a very low frame rate and a very big delay. I cannot argue what's going on.
The only hints I got from logcat are these lines:
2019-05-20 16:37:08.817 29566-29580/it.tux.gcs I/art: Background sticky concurrent mark sweep GC freed 88152(3MB) AllocSpace objects, 3(52KB) LOS objects, 22% free, 7MB/10MB, paused 3.937ms total 111.782ms
2019-05-20 16:37:08.832 29566-29587/it.tux.gcs D/skia: Encode PNG Singlethread : 13003 us, width=160, height=120
even with the sum of all this delay (140 ms) the app should sustain a frame rate of more than 5Hz while am getting 0.25Hz or even worse.
After some investigation I found that moving:
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
out of the nested loops prevent GC from being invoked so frequently and I found another major source of delay in this line:
final String[] points = line.split("\\s+");
it burns out 2ms per time so I decided to go for something less smart but faster:
final String[] points = line.split(" ");
Anyway it's still not enough.. the code between:
canvas.drawBitmap(bitmap, 0, 0, null);
and
sendBroadcast(messageIntent);
still consume more than 200ms ... how can I do better than this?
I'm pretty sure there's a more efficient way to collect a serie of frames of this size and rate from a TCP server and display them on a ImageView.
Of course this can be a matter of software architecture not only optimization of this code itself. I'm open to any new approach besides native code (I'm not familiar with it).
UPDATE (03/11/2019):
Activity side:
public class MainActivity extends AppCompatActivity implements FrameReadyCallBack {
private Intent videoServiceIntent;
private VideoService videoService;
private boolean bound = false;
private ImageView surfaceView_video = null;
private String videoPort = "5002";
private String videoServerAddr = "192.168.10.107";
private ServiceConnection serviceConnection = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
surfaceView_video = findViewById(R.id.surfaceView_video);
serviceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
VideoService.VideoServiceBinder binder = (VideoService.VideoServiceBinder) service;
videoService = binder.getService();
bound = true;
videoService.registerCallBack(MainActivity.this); // register
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
bound = false;
}
};
startVideoService();
}
#Override
public void frameReady(byte[] image_data) {
//TODO: create image and update surfaceView_video
}
public void startVideoService()
{
videoServiceIntent = new Intent(this, VideoService.class);
videoServiceIntent.putExtra(VideoService.LOCAL_PORT_KEY, videoPort);
videoServiceIntent.putExtra(VideoService.LOCAL_VIDEOSERVER_ADDR_KEY, videoServerAddr);
startService(videoServiceIntent);
}
#Override
protected void onStart() {
super.onStart();
bindService();
}
#Override
protected void onStop() {
super.onStop();
unbindService();
}
private void bindService() {
bindService(videoServiceIntent, serviceConnection, Context.BIND_AUTO_CREATE);
}
private void unbindService(){
if (bound) {
videoService.registerCallBack(null); // unregister
unbindService(serviceConnection);
bound = false;
}
}
}
Service side:
public class VideoService extends Service {
public static final String LOCAL_PORT_KEY = "video_port";
public static final String LOCAL_VIDEOSERVER_ADDR_KEY = "video_server_addr";
private static final int DEFAULT_VIDEO_PORT = 5002;
private static final int VIDEO_SERVER_RESPAWN = 2000;
private volatile FrameReadyCallBack frameReadyCallBack = null;
private VideoReceiver videoReceiver = null;
private IBinder videoServiceBinder = new VideoServiceBinder();
#Nullable
#Override
public IBinder onBind(Intent intent) {
return videoServiceBinder ;
}
#Override
public boolean onUnbind(Intent intent) {
videoReceiver.kill();
return super.onUnbind(intent);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
final int localVideoPort = intent.getIntExtra(LOCAL_PORT_KEY, DEFAULT_VIDEO_PORT);
final String videoServerAddr = intent.getStringExtra(LOCAL_VIDEOSERVER_ADDR_KEY);
videoReceiver = new VideoReceiver(videoServerAddr, localVideoPort);
videoReceiver.start();
return Service.START_NOT_STICKY;
}
public void registerCallBack(FrameReadyCallBack frameReadyCallBack) {
this.frameReadyCallBack = frameReadyCallBack;
}
public class VideoServiceBinder extends Binder {
public VideoService getService() {
return VideoService.this;
}
}
private class VideoReceiver extends Thread {
private boolean keepRunning = true;
private int VIDEO_SERVER_PORT;
private String VIDEO_SERVER_ADDR;
private int bad_frames;
private int frames;
private int link_respawn;
private FrameDecodingStatus status;
public VideoReceiver(String addr, int listen_port) {
VIDEO_SERVER_PORT = listen_port;
VIDEO_SERVER_ADDR = addr;
}
public void run() {
InetAddress serverAddr;
link_respawn = 0;
try {
serverAddr = InetAddress.getByName(VIDEO_SERVER_ADDR);
} catch (UnknownHostException e) {
Log.e(getClass().getName(), e.getMessage());
e.printStackTrace();
return;
}
Socket socket = null;
DataInputStream stream;
do {
bad_frames = 0;
frames = 0;
status = FrameDecodingStatus.Idle;
try {
socket = new Socket(serverAddr, VIDEO_SERVER_PORT);
stream = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
final byte[] _data = new byte[PACKET_SIZE];
final byte[] _image_data = new byte[IMAGE_SIZE];
int _data_index = 0;
while (keepRunning) {
if (stream.read(_data, 0, _data.length) == 0)
continue;
for (byte _byte : _data) {
if (status == FrameDecodingStatus.Idle) {
//Wait SoM
} else if (status == FrameDecodingStatus.Data) {
//Collect data
} else {
frameReadyCallBack.frameReady(_image_data);
status = FrameDecodingStatus.Idle;
}
}
}
link_respawn++;
Thread.sleep(VIDEO_SERVER_RESPAWN);
Log.d(getClass().getName(), "Link respawn: " + link_respawn);
} catch (Throwable e) {
Log.e(getClass().getName(), e.getMessage());
e.printStackTrace();
}
} while (keepRunning);
if (socket != null) {
try {
socket.close();
} catch (Throwable e) {
Log.e(getClass().getName(), e.getMessage());
e.printStackTrace();
}
}
}
public void kill() {
keepRunning = false;
}
}
}
First of all, you are submitting results with new image changes via BroadcastReceiver for some reason. You could improve your overall speed significantly, but removing this logic. And replace communication with a Service via bound features.
// Bind to LocalService
Intent intent = new Intent(this, LocalService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
Then receive connection.
/** Defines callbacks for service binding, passed to bindService() */
private ServiceConnection connection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className,
IBinder service) {
// We've bound to LocalService, cast the IBinder and get LocalService instance
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
Then use Service binder instance to subscribe Activity and use callback to in a Service to post new data bytes.
Related
I'm trying to get working a Service that hosts a server, and whenever it receives data from it's one client it sends the data off to another server. Both of which are connected by a tcp socket that remains open indefinitely. I'm having trouble implementing single tcp sockets that both read and write correctly.
I'm receiving XML from both ends, and they're well defined. Some processing is done on the xml received and it needs to be added to a queue that handles it's order.
Ideally the connection going in either direction should remain open indefinitely.
But so far I'm seeing the Sockets just keep closing both this Service and the ServerCode are getting closed sockets and I'm not sure why.
Is there a way to establish connections to my two endpoints and keep the sockets open indefinitely?
public class routing extends Service {
private static final String TAG = "[RoutingService]";
private final IBinder mBinder = new RoutingBinder();
private final ScheduledThreadPoolExecutor mRoutingThreadPool = new ScheduledThreadPoolExecutor(2);
private boolean running = false;
private URI serverAddress;
private URI clientAddress;
private Thread serverServiceThread = new ClientService();
private Thread clientServiceThread = new ServerService();
private PriorityBlockingQueue<String> clientQueue;
private PriorityBlockingQueue<String> serverQueue;
public void setClientAddress(URI testServer) {
this.serverAddress = testServer;
this.mRoutingThreadPool.remove(clientServiceThread);
this.mRoutingThreadPool.scheduleWithFixedDelay(clientServiceThread, 0, 100, TimeUnit.MILLISECONDS);
}
public URI getServerAddress() {
return serverAddress;
}
public void setServerAddress(URI testServer) {
startRunning();
this.serverAddress = testServer;
this.mRoutingThreadPool.remove(serverServiceThread);
this.mRoutingThreadPool.scheduleWithFixedDelay(serverServiceThread, 0, 100, TimeUnit.MILLISECONDS);
}
public void startRunning() {
running = true;
}
public void stopRunning() {
running = false;
}
#Override
public void onCreate() {
super.onCreate();
serverQueue = new PriorityBlockingQueue<>();
clientQueue = new PriorityBlockingQueue<>();
}
#Override
public void onDestroy() {
stopRunning();
super.onDestroy();
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
#Override
public int onStartCommand(#Nullable Intent intent, int flags, int startId) {
clientAddress = URI.create("127.0.0.1:8054");
serverAddress = URI.create("192.168.2.1:7087");
startRunning();
setClientAddress(clientAddress);
setServerAddress(serverAddress);
return Service.START_STICKY;
}
public class RoutingBinder extends Binder {
public routing getService() {
return routing.this;
}
}
class ClientService extends Thread {
private Socket socket;
private Runnable ClientReader = new Runnable() {
#Override
public void run() {
if (socket != null && socket.isConnected()) {
try (InputStreamReader sr = new InputStreamReader(socket.getInputStream())) {
StringBuilder xml = new StringBuilder();
char[] buffer = new char[8192];
String content = "";
int read;
while ((read = sr.read(buffer, 0, buffer.length)) != -1) {
serverQueue.add(new String(buffer));
}
} catch (IOException e) {
Log.e("clientReader", "Error in testReading Thread.", e);
}
}
}
};
private Runnable ClientWriter = new Runnable() {
#Override
public void run() {
if (socket != null && socket.isConnected()) {
while (serverQueue != null && !serverQueue.isEmpty()) {
try (OutputStream os = socket.getOutputStream()) {
String xml = serverQueue.poll();
os.write(xml.getBytes());
os.flush();
} catch (IOException e) {
Log.e("clientWriter", "Error in testReading Thread.", e);
}
}
}
}
};
#Override
public void run() {
try (ServerSocket server = new ServerSocket(clientAddress.getPort())) {
try (Socket socket = server.accept()) {
socket.setSoTimeout(0);
Log.d("SOCKET", String.format("Local Port: %s. Remote Port: %s", socket.getLocalPort(), socket.getPort()));
this.socket = socket;
//Make the Threads
Thread reader = new Thread(ClientReader);
Thread writer = new Thread(ClientWriter);
//Start the Threads
reader.start();
writer.start();
//Start the Server
startRunning();
//Join on the Threads so this driver thread will wait until they finish.
reader.join();
writer.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
stopRunning();
}
}
class ServerService extends Thread {
private Socket socket;
private Runnable ServerReader = new Runnable() {
#Override
public void run() {
if (socket != null && !socket.isClosed()) {
try (InputStreamReader sr = new InputStreamReader(socket.getInputStream())) {
StringBuilder xml = new StringBuilder();
char[] buffer = new char[8192];
String content = "";
int read;
while ((read = sr.read(buffer, 0, buffer.length)) != -1) {
clientQueue.add(new String(buffer));
}
} catch (IOException e) {
Log.e("ServerReader", "Error in testReading Thread.", e);
}
}
}
};
private Runnable ServerWriter = new Runnable() {
#Override
public void run() {
if (socket != null && socket.isConnected()) {
try (OutputStream os = socket.getOutputStream()) {
while (clientQueue != null && !clientQueue.isEmpty()) {
String xml = clientQueue.poll();
os.write(xml.getBytes());
os.flush();
}
} catch (IOException e) {
Log.e("ServerWriter", "Error in testReading Thread.", e);
}
}
}
};
#Override
public void run() {
if (running) { //Service will keep spinning unti the testService ends the loop
try (Socket socket = new Socket(serverAddress.getHost(), serverAddress.getPort())) {
socket.setSoTimeout(0);
Log.d("SOCKET", String.format("Local test Port: %s. Remote test Port: %s", socket.getLocalPort(), socket.getPort()));
this.socket = socket;
//Make the Threads
final Thread writer = new Thread(ServerWriter);
final Thread reader = new Thread(ServerReader);
//Start the Threads
writer.start();
reader.start();
//Join on the Threads so this driver thread will wait until they finish.
writer.join();
reader.join();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
Closing the input or output stream of a socket closes the other stream and the socket.
How do I accurately measure the data rate of the router by using UDP?
My reference is Wi-Fi SweetSpots it measures the data transfer rate of the router locally (not internet speed)
Here's my code now, I'm getting around 700mbs to 800mbs (which is not correct)
public class RouterTransferRateService extends DaggerService {
public static final String ACTION_NAME = "rounter.transter.data.service";
#Inject
WifiManager wifiManager;
#Inject
WirelessInfoProvider wirelessInfoProvider;
private final IBinder binder = new RouterTransferRateDataBinder();
private TimerTask taskReceive;
private Timer timerReceive;
private boolean isSending = false;
private boolean isReceiving = false;
private final int BUF_SIZE = 65507;
private final int serverPort = 50001;
private final long MB = 1024L * 1024L;
private long oldBytes = 0;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
private void initializeTimerTask() {
taskReceive = new TimerTask() {
#Override
public void run() {
long currentBytes = TrafficStats.getUidRxBytes(myUid());
long bytesPerSec = currentBytes - oldBytes;
oldBytes = currentBytes;
Log.e("Data Rate?", (bytesPerSec / MB) + "Mbps");
}
};
new Thread(() -> {
try {
DatagramSocket senderSocket = new DatagramSocket();
senderSocket.setSendBufferSize(BUF_SIZE);
byte[] buf = new byte[BUF_SIZE];
InetAddress address = wirelessInfoProvider.getInetAddress();
new Random().nextBytes(buf);
while (isSending) {
try {
DatagramPacket packet = new DatagramPacket(buf, buf.length, address, serverPort);
senderSocket.send(packet);
} catch (Exception e) {
}
}
senderSocket.disconnect();
senderSocket.close();
} catch (Exception e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
DatagramSocket receiverSocket = new DatagramSocket(serverPort);
receiverSocket.setReceiveBufferSize(BUF_SIZE);
byte[] buffer = new byte[BUF_SIZE];
DatagramPacket packet = new DatagramPacket(buffer, BUF_SIZE);
while (isReceiving) {
receiverSocket.receive(packet);
}
receiverSocket.disconnect();
receiverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
private void startTimer() {
isSending = true;
isReceiving = true;
initializeTimerTask();
timerReceive = new Timer();
timerReceive.schedule(taskReceive, 0, 1000);
}
private void stopTimer() {
isSending = false;
isReceiving = false;
if (timerReceive != null) {
timerReceive.cancel();
timerReceive = null;
}
}
#Override
public void onCreate() {
super.onCreate();
Log.e(getClass().getName(), "onCreate");
startTimer();
}
#Override
public void onDestroy() {
Log.e(getClass().getName(), "onDestroy");
stopTimer();
super.onDestroy();
}
#Override
public ComponentName startService(Intent service) {
return super.startService(service);
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return binder;
}
public class RouterTransferRateDataBinder extends Binder {
public RouterTransferRateService getService() {
return RouterTransferRateService.this;
}
}
}
I want to call non static method in my Activity A from Activity B
like
class A extend Activity(){ public void c(){}}
class B extend Activity(){ A.C(); }
How I could do this in android Activity help me.
public class Voice extends Activity {
TextView resultTEXT ;
MediaPlayer mp;
ImageView view;
private BluetoothAdapter btAdapter = null;
private BluetoothSocket btSocket = null;
int page;
// SPP UUID service
private static final UUID BTMODULEUUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
// String for MAC address
private static String address;
private static String status;
BluetoothDevice device;
private ConnectedThread mConnectedThread;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_voice);
//get the stored mac address of the device
SharedPreferences shared = getSharedPreferences("BtAddress", MODE_PRIVATE);
address = (shared.getString("btAddress", ""));
status = (shared.getString("connect", ""));
btAdapter = BluetoothAdapter.getDefaultAdapter();
//create device and set the MAC address
device = btAdapter.getRemoteDevice(address);
checkBTState();
if(status=="true")
{
new CountDownTimer(1000, 10000) {
public void onTick(long millisUntilFinished) {
}
public void onFinish() {
mp = MediaPlayer.create(Voice.this, R.drawable.onload);
mp.setLooping(false);
mp.start();
}
}.start();
}
view=(ImageView)findViewById(R.id.imageButton);
view.setOnTouchListener(new View.OnTouchListener() {
Handler handler = new Handler();
int numberOfTaps = 0;
long lastTapTimeMs = 0;
long touchDownMs = 0;
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
touchDownMs = System.currentTimeMillis();
break;
case MotionEvent.ACTION_DOWN:
handler.removeCallbacksAndMessages(null);
if ((System.currentTimeMillis() - touchDownMs) > ViewConfiguration.getTapTimeout()) {
//it was not a tap
numberOfTaps = 0;
lastTapTimeMs = 0;
break;
}
if (numberOfTaps > 0
&& (System.currentTimeMillis() - lastTapTimeMs) < ViewConfiguration.getDoubleTapTimeout()) {
numberOfTaps += 1;
} else {
numberOfTaps = 1;
}
lastTapTimeMs = System.currentTimeMillis();
if (numberOfTaps == 2) {
handler.postDelayed(new Runnable() {
#Override
public void run() {
//handle double tap
Toast.makeText(Voice.this, "Help", Toast.LENGTH_LONG).show();
mp = MediaPlayer.create(Voice.this, R.drawable.help);
mp.setLooping(false);
mp.start();
}
}, ViewConfiguration.getDoubleTapTimeout());
}
else if(numberOfTaps== 1)
{
handler.postDelayed(new Runnable() {
#Override
public void run() {
Toast.makeText(Voice.this, "proceed",Toast.LENGTH_LONG).show();
Intent intent=new Intent(Voice.this,ChangeSpeed.class);
startActivity(intent);
}
}, ViewConfiguration.getTapTimeout());
}
}
return true;
}
});
}
public void onActivityResult(int request_result, int result_code, Intent i)
{
super.onActivityResult(result_code, result_code, i);
switch (result_code)
{
case 100: if(result_code == RESULT_OK && i != null)
{
ArrayList<String> result = i.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
resultTEXT.setText(result.get(0));
}
break;
}
}
#Override
public void onResume() {
super.onResume();
try
{
btSocket = createBluetoothSocket(device);
}
catch (IOException e)
{
Toast.makeText(getBaseContext(), "Socket creation failed", Toast.LENGTH_LONG).show();
}
// Establish the Bluetooth socket connection.
try
{
btSocket.connect();
}
catch (IOException e)
{
try
{
btSocket.close();
}
catch (IOException e2)
{
Log.e("",""+e2);
}
}
mConnectedThread = new ConnectedThread(btSocket);
mConnectedThread.start();
// send a character when resuming.beginning transmission to check device is connected
//If it is not an exception will be thrown in the write method and finish() will be called
mConnectedThread.write("x");
}
private BluetoothSocket createBluetoothSocket(BluetoothDevice device) throws IOException {
return device.createRfcommSocketToServiceRecord(BTMODULEUUID);
}
#Override
public void onPause()
{
super.onPause();
try
{
// Bluetooth sockets close when leaving activity
btSocket.close();
} catch (IOException e2)
{
Log.e("",""+e2);
}
}
/*
//Checks that the Android device Bluetooth is available turned on if off automatically
*/
private void checkBTState()
{
if(btAdapter==null)
{
Toast.makeText(getBaseContext(), "Device does not support bluetooth", Toast.LENGTH_LONG).show();
} else
{
if (btAdapter.isEnabled())
{
}
else
{
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, 1);
}
}
}
private class ConnectedThread extends Thread {
// private final InputStream mmInStream;
private final OutputStream mmOutStream;
//creation of the connect thread
public ConnectedThread(BluetoothSocket socket)
{
// InputStream tmpIn = null;
OutputStream tmpOut = null;
try
{
//Create I/O streams for connection
// tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
}
catch (IOException e)
{
}
//mmInStream = tmpIn;
mmOutStream = tmpOut;
}
//write method
public void write(String input)
{
byte[] msgBuffer = input.getBytes();
//converts entered String into bytes
try
{
mmOutStream.write(msgBuffer);
} catch (IOException e)
{
//if you cannot write, close the application
Toast.makeText(getBaseContext(), "Connection Failure", Toast.LENGTH_LONG).show();
finish();
}
}
}
/*
method to on the Fan
*/
public void functionFanOn()
{
mConnectedThread.write("1");
// Send "1" via Bluetooth
Toast.makeText(getBaseContext(), "Turn on Fan", Toast.LENGTH_SHORT).show();
Log.e("", "On");
}
functionFanOn() is the method that I want to call in B
You have a problem with the structure of your code. The function in Activity A, which establishes a connection to the Arduino, should be move in another class. Lets say an Utils class.
Refactoring your common code to an new class, so that you can manager it by single instance
This lib EventBus may solve your issue
Another Way is Create Seprate class and use all method of seprate class
class Utils
{
public void c(Context context)
{
//put your code
}
}
And Activity class Like this
class MyActivity extends Activity
{
public void onCreate(Bundle b)
{
super.onCreate(b);
setContentView(R.layout.main);
//method calling
Utils utils=new utils();
utils.c(MyActivity.this);
}
}
Please read my sample code
Is this one activity class like this and contain c(); method
class A extend Activity
{
public void c()
{
}
}
And This is your Second activity class
class B extend Activity
{
A a=new A();
a.c();
}
i hope this help you
I have 3 activities in my android Application. In the first activity, on the click of a bluetooth device from the list of paired devices, I'm starting a service to keep the bluetooth connection visible to all the actives. In the service class I'm reading data continuously from the bluetooth device and I'm binding the second activity to the service class to read the data received.
I'm not able to get the instance of the binder outside the onServiceConnected() method of service connection method. So I'm calling a user-defined thread from onServiceConnected() method. In this way I'm getting values continuously from the service class. But the app will not respond after few seconds of successful execution.
It is blocking the main thread I think. But I'm not getting where I need to modify my code. The code below is my second Activity(MainActivity). "bluetoothManager" is my service class. I need to do a similar task in third activity also.
I'm not getting whether the problem is with binding or the thread. I need to call the thread outside of the Service connection class. If I do so, I'll get a null pointer exception. So I'm calling the thread from onServiceConnected() function where the binder object is not null. I have to use the boolean mIsBound for the while loop. But now it will be always true. Please help me. I'm new to android.
bluetoothManager.class
public class bluetoothManager extends Service{
final int handlerState = 0; // used to identify handler message
private BluetoothAdapter btAdapter = null;
private BluetoothSocket btSocket = null;
private StringBuilder recDataString = new StringBuilder();
public ConnectedThread mConnectedThread;
static Handler bluetoothIn;
int bp;
String sensor0,sensor1;
static Handler mHandler;
// SPP UUID service - this should work for most devices
private static final UUID BTMODULEUUID = UUID
.fromString("00001101-0000-1000-8000-00805F9B34FB");
IBinder mBinder = new LocalBinder();
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public class LocalBinder extends Binder {
bluetoothManager getService() {
return bluetoothManager.this;
}
}
#Override
public void onCreate() {
/// Toast.makeText(this, " MyService Created ", Toast.LENGTH_LONG).show();
// flag="created";
}
private BluetoothSocket createBluetoothSocket(BluetoothDevice device) throws IOException {
return device.createRfcommSocketToServiceRecord(BTMODULEUUID);
// creates secure outgoing connecetion with BT device using UUID
}
public String getBPM(){
return sensor1;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, " MyService Started", Toast.LENGTH_LONG).show();
final String address=intent.getStringExtra("address");
final int currentId = startId;
if(address!=null)
{
btAdapter = BluetoothAdapter.getDefaultAdapter();
BluetoothDevice device = btAdapter.getRemoteDevice(address);
try {
btSocket = createBluetoothSocket(device);
} catch (IOException e) {
Toast.makeText(getBaseContext(), "Socket creation failed",
Toast.LENGTH_LONG).show();
}
// Establish the Bluetooth socket connection.
try {
btSocket.connect();
} catch (IOException e) {
try {
btSocket.close();
} catch (IOException e2) {
// insert code to deal with this
}
}
mConnectedThread = new ConnectedThread(btSocket);
mConnectedThread.start();
// I send a character when resuming.beginning transmission to check
// device is connected
// If it is not an exception will be thrown in the write method and
// finish() will be called
mConnectedThread.write("x");
}
bluetoothIn = new Handler() {
public void handleMessage(android.os.Message msg) {
if (msg.what == handlerState) { // if message is what we want
String readMessage = (String) msg.obj; // msg.arg1 = bytes
// from connect
// thread
recDataString.append(readMessage); // keep appending to
// string until ~
int endOfLineIndex = recDataString.indexOf("~"); // determine
// the
// end-of-line
if (endOfLineIndex > 0) { // make sure there data before ~
String dataInPrint = recDataString.substring(0,
endOfLineIndex); // extract string
//txtString.setText("Data Received = " + dataInPrint);
/*int dataLength = */dataInPrint.length(); // get length of
// data received
/*txtStringLength.setText("String Length = "
+ String.valueOf(dataLength));*/
if (recDataString.charAt(0) == '#') // if it starts with
// # we know it is
// what we are
// looking for
{
sensor0 = recDataString.substring(1,3);
// get
sensor1=sensor0;
Log.d("bpm", sensor0);
}
recDataString.delete(0, recDataString.length()); // clear
// all
// string
// data
// strIncom =" ";
dataInPrint = " ";
}
}
}
};
// get Bluetooth
// adapter
return currentId;
}
private class ConnectedThread extends Thread {
private final InputStream mmInStream;
private final OutputStream mmOutStream;
// creation of the connect thread
public ConnectedThread(BluetoothSocket socket) {
InputStream tmpIn = null;
OutputStream tmpOut = null;
try {
// Create I/O streams for connection
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
byte[] buffer = new byte[256];
int bytes;
// Keep looping to listen for received messages
while (true) {
try {
bytes = mmInStream.read(buffer); // read bytes from input
// buffer
String readMessage = new String(buffer, 0, bytes);
// Send the obtained bytes to the UI Activity via handler
bluetoothIn.obtainMessage(handlerState, bytes, -1,
readMessage).sendToTarget();
} catch (IOException e) {
break;
}
}
}
// write method
public void write(String input) {
byte[] msgBuffer = input.getBytes(); // converts entered String into
// bytes
try {
mmOutStream.write(msgBuffer); // write bytes over BT connection
// via outstream
} catch (IOException e) {
// if you cannot write, close the application
Toast.makeText(getBaseContext(), "Connection Failure",
Toast.LENGTH_LONG).show();
}
}
}
#Override
public void onRebind(Intent intent) {
Log.v("myservice", "in onRebind");
super.onRebind(intent);
}
#Override
public boolean onUnbind(Intent intent) {
Log.v("myapp", "in onUnbind");
return true;
}
#Override
public void onDestroy() {
super.onDestroy();
Log.v("myservice", "in onDestroy");
}
}
MainActivity.java
public class MainActivity extends Activity {
private ServiceConnection mConnection;
TextView sensorView0;
boolean mIsBound;
bluetoothManager bm;
private Handler bpmHandler;
private ServiceConnection mConnection;
final int handlerState = 0; // used to identify handler message
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
sensorView0 = (TextView) findViewById(R.id.bpm);
bpmHandler=new Handler(){
public void handleMessage(android.os.Message msg) {
if (msg.what == handlerState) {
String s=(String)msg.obj;
sensorView0.setText("BPM="+s);
}
}
};
}
#Override
public void onResume() {
super.onResume();
mConnection= new ServiceConnection() {
#Override
public void onServiceDisconnected(ComponentName name) {
mIsBound = false;
bm=null;
}
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
LocalBinder myBinder = (LocalBinder)service;
mIsBound = true;
bm=myBinder.getService();
mConnectedService=new ConnectedService(mIsBound);
mConnectedService.start();
}
};
Intent intent = new Intent(this, bluetoothManager.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
private class ConnectedService extends Thread {
final boolean bound;
public ConnectedService(boolean mIsBound){
bound =mIsBound;
}
public void run() {
String s;
while (bound) {
s= bm.getBPM();
Message msg = new Message();
msg.what =handlerState ;
msg.obj=s; MainActivity.this.bpmHandler.sendMessage(msg);
}
}
};
#Override
public void onPause() {
super.onPause();
unbindService(mConnection);
mIsBound = false;
}
}
I feel the connectedservice thread code is causing the issue. Instead of continuously racing grtBPM method, why don't you post the message only when there is a change. You can use local broadcast manager to broadcast the message from service and catch that in activity and update UI accordingly. The connectedservice thread runs continuously and keep posting the message to handler which is causing load on the main thread.
I am attempting to connect to my XAMPP server and interact with the MySQL database with the classes below. However, the error notes that I receive a NullPointerException at the line:
result = imService.createNewGroup(newGroupName);
In the CreateGroup class. It should be noted that the CreateGroup class is also called right after a user inputs text into a Dialog and the service is started from there. I am fairly new to services and network connections, but is there something I'm missing that should allow to at least verify that the service is connected before trying to send the .createGroup command?
CreateGroup Class:
public class CreateGroup extends Activity {
private static final String SERVER_RES_RES_SIGN_UP_SUCCESFULL = "1";
private static final String SERVER_RES_SIGN_UP_USERNAME_CRASHED = "2";
private Manager imService;
private Handler handler = new Handler();
String newGroupName;
public ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
imService = ((MessagingService.IMBinder) service).getService();
}
public void onServiceDisconnected(ComponentName className) {
imService = null;
Toast.makeText(CreateGroup.this, R.string.local_service_stopped,
Toast.LENGTH_SHORT).show();
}
};
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
bindService(new Intent(CreateGroup.this, MessagingService.class),
mConnection, Context.BIND_AUTO_CREATE);
// Getting intent and info from the dialog
Intent i = getIntent();
Bundle extras = i.getExtras();
newGroupName = extras.getString("groupName");
Thread thread = new Thread() {
String result = new String();
#Override
public void run() {
// Send group name to the messaging
// service
try {
result = imService.createNewGroup(newGroupName);
} catch (NullPointerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Log.d("problem", "The value of result is " + result.toString());
handler.post(new Runnable() {
#Override
public void run() {
if (result == null) {
Toast.makeText(getApplicationContext(),
"It's null, not working", Toast.LENGTH_LONG)
.show();
}
if (result != null
&& result
.equals(SERVER_RES_RES_SIGN_UP_SUCCESFULL)) {
Toast.makeText(getApplicationContext(),
R.string.signup_successfull,
Toast.LENGTH_LONG).show();
// showDialog(SIGN_UP_SUCCESSFULL);
} else if (result != null
&& result
.equals(SERVER_RES_SIGN_UP_USERNAME_CRASHED)) {
Toast.makeText(getApplicationContext(),
R.string.signup_username_crashed,
Toast.LENGTH_LONG).show();
// showDialog(SIGN_UP_USERNAME_CRASHED);
} else // if
// (result.equals(SERVER_RES_SIGN_UP_FAILED))
{
Toast.makeText(getApplicationContext(),
R.string.signup_failed, Toast.LENGTH_LONG)
.show();
// showDialog(SIGN_UP_FAILED);
}
}
});
}
};
thread.start();
}
Server Case for "createGroup" method:
case "createGroup":
$SQLtest = "insert into groups(groupName, uniqueGroup, createTime)
VALUES('TestGroup', 1234567891, NOW())";
error_log("$SQLtest", 3 , "error_log");
if($result = $db -> query($SQLtest))
{
$out = SUCCESSFUL;
}
else
{
$out = FAILED;
}
break;
Messaging Service and createGroup method:
public class MessagingService extends Service implements Manager, Updater {
// private NotificationManager mNM;
public static String USERNAME;
public static final String TAKE_MESSAGE = "Take_Message";
public static final String FRIEND_LIST_UPDATED = "Take Friend List";
public static final String MESSAGE_LIST_UPDATED = "Take Message List";
public ConnectivityManager conManager = null;
private final int UPDATE_TIME_PERIOD = 15000;
private String rawFriendList = new String();
private String rawMessageList = new String();
SocketerInterface socketOperator = new Socketer(this);
private final IBinder mBinder = new IMBinder();
private String username;
private String password;
private boolean authenticatedUser = false;
// timer to take the updated data from server
private Timer timer;
private StorageManipulater localstoragehandler;
private NotificationManager mNM;
public class IMBinder extends Binder {
public Manager getService() {
return MessagingService.this;
}
}
#Override
public void onCreate() {
mNM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
localstoragehandler = new StorageManipulater(this);
// Display a notification about us starting. We put an icon in the
// status bar.
// showNotification();
conManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
new StorageManipulater(this);
// Timer is used to take the friendList info every UPDATE_TIME_PERIOD;
timer = new Timer();
Thread thread = new Thread() {
#Override
public void run() {
Random random = new Random();
int tryCount = 0;
while (socketOperator.startListening(10000 + random
.nextInt(20000)) == 0) {
tryCount++;
if (tryCount > 10) {
// if it can't listen a port after trying 10 times, give
// up...
break;
}
}
}
};
thread.start();
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
#Override
public String createNewGroup(String groupName) throws NullPointerException, UnsupportedEncodingException {
String params = "action=createGroup";
String result = socketOperator.sendHttpRequest(params);
return result;
}
}
Because your code has an inherent race condition. And an evil one.
Change to something like this:
public void onCreate(Bundle savedInstanceState) {
bindService(new Intent(CreateGroup.this, MessagingService.class),
mConnection, Context.BIND_AUTO_CREATE);
// but do not start thread here!
}
public ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
imService = ((MessagingService.IMBinder) service).getService();
startCommunicationThread(); // <----------------------- only here can you start comm. thread
}
public void onServiceDisconnected(ComponentName className) {
imService = null;
Toast.makeText(CreateGroup.this, R.string.local_service_stopped,
Toast.LENGTH_SHORT).show();
}
};
void startCommunicationThread() {
Thread thread = new Thread() {
String result = new String();
#Override
public void run() {
try {
result = imService.createNewGroup(newGroupName);
..........
}
If you want your code to be even more secure, use a connection state field:
public ServiceConnection mConnection = new ServiceConnection() {
volatile boolean isConnected;
public void onServiceConnected(ComponentName className, IBinder service) {
isConnected = true; // <---------------------
imService = ((MessagingService.IMBinder) service).getService();
startCommunicationThread();
}
public void onServiceDisconnected(ComponentName className) {
isConnected = false; // <---------------
imService = null;
Toast.makeText(CreateGroup.this, R.string.local_service_stopped,
Toast.LENGTH_SHORT).show();
}
};
And poll isConnected from within startCommunicationThread to make sure no sudden disconnects.
in my project MessagingService.IMBinder MessagingService gives error is there any java class that I should import.